diff --git a/ phpunit.dusk.xml b/ phpunit.dusk.xml new file mode 100644 index 00000000..db1abe1e --- /dev/null +++ b/ phpunit.dusk.xml @@ -0,0 +1,16 @@ + + + + + + + + + ./tests/Browser/DryRun/ + + + + + diff --git a/.env.dusk b/.env.dusk new file mode 100644 index 00000000..4041ae24 --- /dev/null +++ b/.env.dusk @@ -0,0 +1,59 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY=base64:X521H/hWdbsG6S/JG0Q/BZgTo1azoV18kzqkqMQSDrQ= +APP_DEBUG=true +APP_URL=http://localhost + +LOG_CHANNEL=stack + +DB_CONNECTION=dusk +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=exchange-test +DB_USERNAME=root +DB_PASSWORD= + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=smtp.mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS=null +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_APP_CLUSTER=mt1 + +MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +FILESYSTEM_DRIVER="documents" + +JWT_SECRET= +JWT_TTL=1440 + +SHIPPING_URL=http://shipping-portal.test/ +MIX_SHIPPING_URL="${SHIPPING_URL}" + +BILLPLZ_BASE_URL="https://www.billplz-sandbox.com" +BILLPLZ_API_KEY="0fa4c710-761b-4a7a-a501-c2c2d02643d5" +BILLPLZ_X_SIGNATURE_KEY="S-pbNVthVRsvnPfZlgLwqqOg" +BILLPLZ_COLLECTION_ID="hev2wdjy" \ No newline at end of file diff --git a/.env.example b/.env.example index 544ed6cc..428d747b 100644 --- a/.env.example +++ b/.env.example @@ -57,3 +57,14 @@ BILLPLZ_API_KEY="d25c6170-dc00-45cd-b226-d702b957870c" 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/.gitignore b/.gitignore index 476bf6a0..095342e0 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,6 @@ gox.iml rebuild_docker.sh docker/* db/* -docker-compose.yml package-lock.json public/* /public/* 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/Abstracts/AbstractControllerLogic.php b/app/Classes/General/Abstracts/AbstractControllerLogic.php index 2fdd0dab..815219c8 100644 --- a/app/Classes/General/Abstracts/AbstractControllerLogic.php +++ b/app/Classes/General/Abstracts/AbstractControllerLogic.php @@ -4,7 +4,8 @@ namespace App\Classes\General\Abstracts; use App\Classes\Exceptions\ErrorException; -use App\Classes\Exceptions\InternalServerErrorException; +use App\Classes\Jobs\UserRiskAnalysis; +use App\Classes\Modules\Segments\Processors\RiskAnalysisProcessor; use App\Classes\ValueObjects\Constants\Notifications; use App\Classes\ValueObjects\Constants\HttpStatus; use App\Classes\ValueObjects\Response\ApiResponseObject; @@ -13,7 +14,9 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\ResourceCollection; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; abstract class AbstractControllerLogic { @@ -49,9 +52,12 @@ abstract class AbstractControllerLogic * @return JsonResponse */ public function execute(Request $request) : JsonResponse { - try { + if(Auth::user()){ + if(Auth::user()->type === 3) UserRiskAnalysis::dispatch(Auth::user(), $request->header('captcha-token')); + } + DB::beginTransaction(); $response = $this->logic($request); @@ -61,6 +67,7 @@ abstract class AbstractControllerLogic return $response; } catch (ErrorException|GeneralExceptions $exception){ + log::error($exception); return (new ApiResponseObject($this->getNotificationTitle().' failed', $exception->getMessage(), $exception->getCode() ? $exception->getCode() : HttpStatus::SERVER_ERROR))->handler(); @@ -94,4 +101,4 @@ abstract class AbstractControllerLogic return $this->response(json_decode($collection->response()->getContent(), true)); } -} \ No newline at end of file +} diff --git a/app/Classes/General/Eloquent/AbstractListRecord.php b/app/Classes/General/Eloquent/AbstractListRecord.php index c5d2e6e6..7b7d0df9 100644 --- a/app/Classes/General/Eloquent/AbstractListRecord.php +++ b/app/Classes/General/Eloquent/AbstractListRecord.php @@ -6,6 +6,7 @@ namespace App\Classes\General\Eloquent; use App\Classes\Exceptions\MalformedRequestException; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\QueryException; +use Illuminate\Support\Facades\Log; abstract class AbstractListRecord extends AbstractGetRecord { @@ -23,6 +24,7 @@ abstract class AbstractListRecord extends AbstractGetRecord return $this->handler($filters); } catch (QueryException $exception){ + log::error($exception); throw new MalformedRequestException('Unable to fetch the list of records due to unexpected error'); } @@ -43,4 +45,4 @@ abstract class AbstractListRecord extends AbstractGetRecord } -} \ No newline at end of file +} 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/CompanyIdNotIn.php b/app/Classes/General/Eloquent/Filters/CompanyIdNotIn.php new file mode 100644 index 00000000..bde6e0ea --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/CompanyIdNotIn.php @@ -0,0 +1,20 @@ +whereNotIn('company_id', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/CreatedAtBetween.php b/app/Classes/General/Eloquent/Filters/CreatedAtBetween.php new file mode 100644 index 00000000..4aeebb8c --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/CreatedAtBetween.php @@ -0,0 +1,20 @@ +whereBetween('created_at', $value); + } + +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/CurrencyRateIdIn.php b/app/Classes/General/Eloquent/Filters/CurrencyRateIdIn.php new file mode 100644 index 00000000..f80d0855 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/CurrencyRateIdIn.php @@ -0,0 +1,20 @@ +whereIn('currency_rate_id', $value); + } + +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/DateEnd.php b/app/Classes/General/Eloquent/Filters/DateEnd.php new file mode 100644 index 00000000..3a5608f7 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/DateEnd.php @@ -0,0 +1,18 @@ +whereDate('created_at', '<=', $value); + } +} \ No newline at end of file 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/DateStart.php b/app/Classes/General/Eloquent/Filters/DateStart.php new file mode 100644 index 00000000..21979a92 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/DateStart.php @@ -0,0 +1,18 @@ +whereDate('created_at', '>=', $value); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/DoesNotHavePurchaseOrderStatusIn.php b/app/Classes/General/Eloquent/Filters/DoesNotHavePurchaseOrderStatusIn.php new file mode 100644 index 00000000..8f8d305e --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/DoesNotHavePurchaseOrderStatusIn.php @@ -0,0 +1,24 @@ +whereDoesntHave('transactions', function($transaction) use($value) { + return $transaction->where('transactions.type', TransactionType::PURCHASE_ORDER)->whereIn('transactions.status', $value); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/DoesNotHaveTransactionType.php b/app/Classes/General/Eloquent/Filters/DoesNotHaveTransactionType.php new file mode 100644 index 00000000..22ed9573 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/DoesNotHaveTransactionType.php @@ -0,0 +1,22 @@ +whereDoesntHave('transactions', function($query) use($value) { + return $query->where('transactions.type', $value); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/EmailLike.php b/app/Classes/General/Eloquent/Filters/EmailLike.php new file mode 100644 index 00000000..2151b1c8 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/EmailLike.php @@ -0,0 +1,20 @@ +where('email', 'LIKE', '%'.$value.'%'); + } + +} \ No newline at end of file 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/HasPaymentStatusIn.php b/app/Classes/General/Eloquent/Filters/HasPaymentStatusIn.php new file mode 100644 index 00000000..158dc0a5 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/HasPaymentStatusIn.php @@ -0,0 +1,23 @@ +whereHas('transactions', function($query) use($value) { + return $query->where('transactions.type', TransactionType::PAYMENT)->whereIn('transactions.status', $value); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/HasPurchaseOrderStatusIn.php b/app/Classes/General/Eloquent/Filters/HasPurchaseOrderStatusIn.php new file mode 100644 index 00000000..bdc32fa1 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/HasPurchaseOrderStatusIn.php @@ -0,0 +1,23 @@ +whereHas('transactions', function($query) use($value) { + return $query->where('transactions.type', TransactionType::PURCHASE_ORDER)->whereIn('transactions.status', $value); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/HasTransactionType.php b/app/Classes/General/Eloquent/Filters/HasTransactionType.php new file mode 100644 index 00000000..7b277dae --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/HasTransactionType.php @@ -0,0 +1,22 @@ +has('transactions', function($query) use($value) { + return $query->where('transactions.type', $value); + }); + } +} 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/ReferenceLike.php b/app/Classes/General/Eloquent/Filters/ReferenceLike.php new file mode 100644 index 00000000..25f8373c --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/ReferenceLike.php @@ -0,0 +1,20 @@ +where('reference', 'LIKE', '%'.$value.'%'); + } + +} \ No newline at end of file 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/TypeIn.php b/app/Classes/General/Eloquent/Filters/TypeIn.php new file mode 100644 index 00000000..3a064f7a --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/TypeIn.php @@ -0,0 +1,20 @@ +whereIn('type', $value); + } + +} \ No newline at end of file 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/Eloquent/Filters/WhereHasSeasonalSegment.php b/app/Classes/General/Eloquent/Filters/WhereHasSeasonalSegment.php new file mode 100644 index 00000000..78905650 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/WhereHasSeasonalSegment.php @@ -0,0 +1,20 @@ +whereHas('seasonalSegments'); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/WithWallets.php b/app/Classes/General/Eloquent/Filters/WithWallets.php new file mode 100644 index 00000000..5b002922 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/WithWallets.php @@ -0,0 +1,20 @@ +with('wallets'); + } +} diff --git a/app/Classes/General/Helper.php b/app/Classes/General/Helper.php index cad68208..ac89540b 100644 --- a/app/Classes/General/Helper.php +++ b/app/Classes/General/Helper.php @@ -2,6 +2,7 @@ namespace App\Classes\General; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; @@ -24,4 +25,21 @@ class Helper return array_slice(get_class_methods($className), 1); } -} \ No newline at end of file + /** + * @param $log + * @return array + */ + static function debugLogger($log){ + if($log && isset($log['message'])){ + $message = $log['message']; + $substring = 'No data were found'; + if (isset($message)) { + if (strpos($message, $substring) !== false) { + // Log::info('Message exist'); + } else { + Log::info($log['message']); + } + } + } + } +} diff --git a/app/Classes/General/Interfaces/Notifiable.php b/app/Classes/General/Interfaces/Notifiable.php new file mode 100644 index 00000000..e9d8f22d --- /dev/null +++ b/app/Classes/General/Interfaces/Notifiable.php @@ -0,0 +1,15 @@ +table).'_logs'; + $relationshipColumn = Str::singular($model->table).'_id'; + + $originalData = $model->getRawOriginal(); + + $originalData[$relationshipColumn] = $originalData['id']; + unset($originalData['id']); + + // remove pivot columns + foreach($originalData as $key => $row){ + if(str::startsWith($key, 'pivot_')){ + unset($originalData[$key]); + } + } + + if (!Schema::hasTable($tableName)) { + DB::statement('CREATE TABLE '.$tableName.' LIKE '.$model->table); + + $indexs = DB::select('SHOW INDEX FROM '.$tableName.';'); + + $removedIndexes = []; + foreach ($indexs as $index){ + if($index->Column_name === 'id' || in_array($index->Key_name, $removedIndexes)) continue; + DB::statement('ALTER TABLE '.$tableName.' drop index '.$index->Key_name); + $removedIndexes[] = $index->Key_name; + } + + DB::statement('ALTER TABLE '.$tableName.' ADD COLUMN `'.$relationshipColumn.'` BIGINT NOT NULL AFTER `id`'); + } + + DB::table($tableName)->insert($originalData); + + }); + } +} diff --git a/app/Classes/General/VoucherifyHelper.php b/app/Classes/General/VoucherifyHelper.php new file mode 100644 index 00000000..83e38482 --- /dev/null +++ b/app/Classes/General/VoucherifyHelper.php @@ -0,0 +1,14 @@ +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/CreatePerfexCRMCustomer.php b/app/Classes/Jobs/CreatePerfexCRMCustomer.php new file mode 100644 index 00000000..4d7acddd --- /dev/null +++ b/app/Classes/Jobs/CreatePerfexCRMCustomer.php @@ -0,0 +1,41 @@ +createLeadPerfexCRMObject = $createLeadPerfexCRMObject; + } + + public function handle() + { + $lead = (App()->make(FetchesPerfexCRMLead::class))->execute($this->createLeadPerfexCRMObject->getEmail()); + if(!is_null($lead)){ + (App()->make(UpdatesPerfexCRMLead::class))->execute($lead, $this->createLeadPerfexCRMObject->getCompanyReference()); + } + else{ + (App()->make(CreatePerfexCRMLeadProcessor::class))->execute($this->createLeadPerfexCRMObject); + } + } +} diff --git a/app/Classes/Jobs/CreatePerfexCRMInvoice.php b/app/Classes/Jobs/CreatePerfexCRMInvoice.php new file mode 100644 index 00000000..7246dc79 --- /dev/null +++ b/app/Classes/Jobs/CreatePerfexCRMInvoice.php @@ -0,0 +1,32 @@ +transaction = $transaction; + } + + public function handle() + { + (App()->make(CreatePerfexCRMInvoiceProcessor::class))->execute($this->transaction); + } +} diff --git a/app/Classes/Jobs/CreatePerfexCRMSingleTask.php b/app/Classes/Jobs/CreatePerfexCRMSingleTask.php new file mode 100644 index 00000000..586efe0f --- /dev/null +++ b/app/Classes/Jobs/CreatePerfexCRMSingleTask.php @@ -0,0 +1,42 @@ +createTaskPerfexCRMObject = $createTaskPerfexCRMObject; + } + + public function handle() + { + $lead = (App()->make(FetchesPerfexCRMLead::class))->execute($this->createTaskPerfexCRMObject->getEmail()); + if(!is_null($lead)) + { + $this->createTaskPerfexCRMObject->setLeadId($lead->id); + (App()->make(CreatePerfexCRMTaskProcessor::class))->execute($this->createTaskPerfexCRMObject); + } + } +} diff --git a/app/Classes/Jobs/GenerateGroupTransactionsPurchaseOrder.php b/app/Classes/Jobs/GenerateGroupTransactionsPurchaseOrder.php new file mode 100644 index 00000000..970b577b --- /dev/null +++ b/app/Classes/Jobs/GenerateGroupTransactionsPurchaseOrder.php @@ -0,0 +1,22 @@ +make(GeneratesGroupTransactionsPurchaseOrder::class))->execute(); + } +} diff --git a/app/Classes/Jobs/GenerateGroupTransactionsWhiteForm.php b/app/Classes/Jobs/GenerateGroupTransactionsWhiteForm.php new file mode 100644 index 00000000..97475dac --- /dev/null +++ b/app/Classes/Jobs/GenerateGroupTransactionsWhiteForm.php @@ -0,0 +1,22 @@ +make(GeneratesGroupTransactionsWhiteForm::class))->execute(); + } +} diff --git a/app/Classes/Jobs/GenerateInvoice.php b/app/Classes/Jobs/GenerateInvoice.php new file mode 100644 index 00000000..83191e25 --- /dev/null +++ b/app/Classes/Jobs/GenerateInvoice.php @@ -0,0 +1,43 @@ +booking = $booking; + } + + + public function handle() + { + $this->booking->transactions()->whereIn('transactions.type', [TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVER])->delete(); + $this->booking->documents()->whereIn('document_type', [DocumentType::INVOICE, DocumentType::PURCHASE_ORDER, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->delete(); + $this->booking->status = ApprovalStatus::APPROVED; + $this->booking->save(); + + (App()->make(CreateInvoiceTransactionProcessor::class))->execute($this->booking); + } +} 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/UpdatePerfexCRM.php b/app/Classes/Jobs/UpdatePerfexCRM.php new file mode 100644 index 00000000..51ec264f --- /dev/null +++ b/app/Classes/Jobs/UpdatePerfexCRM.php @@ -0,0 +1,74 @@ +updatePerfexCRMObject = $updatePerfexCRMObject; + $this->transaction = $transaction; + $this->shouldCreateInvoice = $shouldCreateInvoice; + } + + + public function handle() + { + //To use invoice as a reference to decide whether more tasks should be created + $this->updatePerfexCRMObject->setInvoiceId($this->getInvoiceIdOrCreateInvoice()); + + $result = (App()->make(UpdatePerfexCRMProcessor::class))->execute($this->updatePerfexCRMObject); + if($this->transaction != null && $this->shouldCreateInvoice){ + $updatePerfexCRMInvoiceObject = new UpdatePerfexCRMInvoiceObject( + $this->updatePerfexCRMObject->getContactEmail(), + $this->updatePerfexCRMObject->getProjectName(), + $result->projectId, + $this->transaction, + true + ); + UpdatePerfexCRMInvoice::dispatch($updatePerfexCRMInvoiceObject); + } + } + + private function getInvoiceIdOrCreateInvoice(){ + if($this->transaction != null){ + $fetchPerfexCRMInvoiceObject = new FetchPerfexCRMInvoiceObject( + $this->updatePerfexCRMObject->getContactEmail(), + $this->transaction + ); + $invoiceId = (App()->make(FetchPerfexCRMInvoiceProcessor::class))->execute($fetchPerfexCRMInvoiceObject); + return $invoiceId; + } + return 0; + } +} diff --git a/app/Classes/Jobs/UpdatePerfexCRMInvoice.php b/app/Classes/Jobs/UpdatePerfexCRMInvoice.php new file mode 100644 index 00000000..26131bca --- /dev/null +++ b/app/Classes/Jobs/UpdatePerfexCRMInvoice.php @@ -0,0 +1,101 @@ +updatePerfexCRMInvoiceObject = $updatePerfexCRMInvoiceObject; + } + + public function handle() + { + //get the client id + $customer = (App()->make(FetchesPerfexCRMCustomer::class))->execute($this->updatePerfexCRMInvoiceObject->getEmail()); + $transaction = $this->updatePerfexCRMInvoiceObject->getTransaction(); + + //get the invoice id + $invoiceId = 0; + $invoiceStatus = 0; + + $number = $transaction->bill_no; + + $prefix = "INV-"; + //This will remove the prefix if prefix already exist in the string + if (substr($number, 0, strlen($prefix)) == $prefix) { + $number = substr($number, strlen($prefix)); + } + + $number = 'EXC-'.$number; + $invoice = (App()->make(FetchesPerfexCRMInvoice::class))->execute($customer->userid,"INV-", $number); + Log::error(json_encode('UpdatePerfexCRMInvoice debug $number: '.$number)); + + if(is_null($invoice)){ + $result = (App()->make(CreatePerfexCRMInvoiceProcessor::class))->execute($transaction); + if ($result) { + $invoiceId = $result->payload['id']; + } else { + // Log::error(json_encode('UpdatePerfexCRMInvoice CreatePerfexCRMInvoiceProcessor failed')); + $log['message'] = 'UpdatePerfexCRMInvoice CreatePerfexCRMInvoiceProcessor failed'; + Helper::debugLogger($log); + } + } + else{ + $invoiceId = $invoice->id; + $invoiceStatus = $invoice->status; + + //This only run when invoice already exist and the invoice does not have a PAID status + if($invoiceStatus != PerfexCRMInvoiceStatus::PAID){ + Log::error(json_encode('UpdatePerfexCRMInvoice debug $this->updatePerfexCRMInvoiceObject->getProjectId(): '.$this->updatePerfexCRMInvoiceObject->getProjectId())); + + //update invoice + (App()->make(UpdatesPerfexCRMInvoice::class))->execute($invoice, $this->updatePerfexCRMInvoiceObject->getProjectId()); + } + } + + //create invoice payment + if($invoiceId != 0 && $invoiceStatus != PerfexCRMInvoiceStatus::PAID){ + + $date = Carbon::parse($transaction->created_at)->format('Y-m-d'); + + $invoicePaymentPerfexCRMObject = new InvoicePaymentPerfexCRMObject( + $invoiceId, + "$transaction->amount", + $date, + 1, + "", + "" + ); + (App()->make(CreatesPerfexCRMInvoicePayment::class))->execute($invoicePaymentPerfexCRMObject); + } + } +} diff --git a/app/Classes/Jobs/UpdatePerfexCRMPrelude.php b/app/Classes/Jobs/UpdatePerfexCRMPrelude.php new file mode 100644 index 00000000..3b05ac63 --- /dev/null +++ b/app/Classes/Jobs/UpdatePerfexCRMPrelude.php @@ -0,0 +1,105 @@ +transaction = $transaction; + $this->updatePerfexCRMObject = $updatePerfexCRMObject; + $this->shouldCreateInvoice = $shouldCreateInvoice; + } + + + public function handle() + { + $serviceTypeName = $this->transaction->owner->company->services()->where('id', $this->transaction->owner->service_id)->first()->name; + $booking = $this->transaction->booking; + $bankDetails = $this->generateBankDetails($booking->bank); + + $data = [ + 'amount' => number_format($this->transaction->amount, 2, '.', ''), + 'currency' => $this->transaction->currency_id == 1 ? "MYR" : "CNY", + 'service_type' => $serviceTypeName, + 'bank_details' => $bankDetails, + 'link_transfer' => config('app.url').'/transfer/'.$this->transaction->owner->marking, + 'link_payments' => config('app.url').'/payments', + 'link_autocount_or' => 'https://docs.google.com/spreadsheets/d/1Q3rJBGQ9Bo04HZp5WR9zbLp7pIW2kfDYsabxG7JON-4/edit#gid=2013983170', + ]; + + $result = $this->replacePlaceholders($this->updatePerfexCRMObject->getTasks(), $data); + $this->updatePerfexCRMObject->setTasks($result); + + UpdatePerfexCRM::dispatch($this->updatePerfexCRMObject, $this->transaction, $this->shouldCreateInvoice); + } + + + private function generateBankDetails($bank): array + { + return $this->transaction->owner->service_id == 4 ? + [ + '1688_username' => $bank->account_no, + '1688_password' => $bank->holder_name, + 'payment_pin' => $bank->bank_branch, + ]: + [ + 'id' => $bank->id, + 'type' => $bank->type, + 'reference' => $bank->reference, + 'bank_name' => $bank->bank_name, + 'bank_branch' => $bank->bank_branch, + 'holder_name' => $bank->holder_name, + 'account_no' => $bank->account_no, + 'country_id' => $bank->country_id, + 'default' => $bank->default, + 'status' => $bank->status, + ]; + } + + + function replacePlaceholders($template, $data, $prefix = '') + { + foreach ($template as $key => $value) { + foreach ($data as $dataKey => $dataValue) { + if (is_array($dataValue)) { + $template[$key] = $this->replacePlaceholders($template[$key], $dataValue, $dataKey); + } else { + if ($prefix != '') { + $template[$key] = str_replace('{' .$prefix. "." .$dataKey . '}', $dataValue, $template[$key]); + } else { + $template[$key] = str_replace('{' . $dataKey . '}', $dataValue, $template[$key]); + } + } + } + } + + return $template; + } + +} diff --git a/app/Classes/Jobs/UserRiskAnalysis.php b/app/Classes/Jobs/UserRiskAnalysis.php new file mode 100644 index 00000000..705acf15 --- /dev/null +++ b/app/Classes/Jobs/UserRiskAnalysis.php @@ -0,0 +1,40 @@ +user = $user; + $this->captchaToken = $captchaToken; + } + + + public function handle() + { + (app()->make(RiskAnalysisProcessor::class))->execute($this->user, $this->captchaToken); + } +} 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 3fe7b382..fa6a9ae7 100644 --- a/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php +++ b/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php @@ -10,16 +10,26 @@ use App\Classes\Modules\Accounts\Processors\GenerateEmailVerificationAttemptProc 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\Companies\DataTransferObjects\EmploymentObject; use App\Classes\Modules\Contacts\Processors\CreateContactProcessor; +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; 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 { @@ -55,6 +65,18 @@ class CreateCustomerLogic extends AbstractControllerLogic /** @var GenerateEmailVerificationAttemptProcessor */ private $generateEmailVerificationAttemptProcessor; + /** @var CreatesSeasonalSegment */ + private $createsSeasonalSegment; + + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; + + /** @var NewCustomerToVoucherifyProcessor */ + private $newCustomerToVoucherifyProcessor; + + /** @var CreateVoucherProcessor */ + private $createVoucherProcessor; + /** * CreateCustomerLogic constructor. * @param CreateUserProcessor $createUserProcessor @@ -64,8 +86,13 @@ class CreateCustomerLogic extends AbstractControllerLogic * @param AssignSegmentProcessor $assignSegmentProcessor * @param AuthenticationProcessor $authenticationProcessor * @param GenerateEmailVerificationAttemptProcessor $generateEmailVerificationAttemptProcessor + * @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) + public function __construct(CreateUserProcessor $createUserProcessor, CreateCompanyProcessor $createCompanyProcessor, CreateContactProcessor $createContactProcessor, AssignEmployeeProcessor $assignEmployeeProcessor, AssignSegmentProcessor $assignSegmentProcessor, AuthenticationProcessor $authenticationProcessor, GenerateEmailVerificationAttemptProcessor $generateEmailVerificationAttemptProcessor, + CreatesSeasonalSegment $createsSeasonalSegment, CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor, NewCustomerToVoucherifyProcessor $newCustomerToVoucherifyProcessor, CreateVoucherProcessor $createVoucherProcessor) { $this->createUserProcessor = $createUserProcessor; $this->createCompanyProcessor = $createCompanyProcessor; @@ -74,6 +101,10 @@ class CreateCustomerLogic extends AbstractControllerLogic $this->assignSegmentProcessor = $assignSegmentProcessor; $this->authenticationProcessor = $authenticationProcessor; $this->generateEmailVerificationAttemptProcessor = $generateEmailVerificationAttemptProcessor; + $this->createsSeasonalSegment = $createsSeasonalSegment; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; + $this->newCustomerToVoucherifyProcessor = $newCustomerToVoucherifyProcessor; + $this->createVoucherProcessor = $createVoucherProcessor; } /** @@ -87,22 +118,50 @@ 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); /** @var Company $company */ - $company = $this->createCompanyProcessor->execute($request, BusinessType::IMPORTER, $request->input('type'), ApprovalStatus::PENDING_SUBMISSION); + $company = $this->createCompanyProcessor->execute($request, BusinessType::IMPORTER, $request->input('type') === CompanyType::COMPANY_BUSINESS ? CompanyType::COMPANY_BUSINESS : CompanyType::PERSONAL_BUSINESS, ApprovalStatus::PENDING_SUBMISSION); $this->createContactProcessor->execute($request, $company); $Object = new EmploymentObject($company, $user); $this->assignEmployeeProcessor->execute($Object); + // assign STANDARD_SEGMENT to all new customers $this->assignSegmentProcessor->execute($company); - $this->generateEmailVerificationAttemptProcessor->execute($user); + // 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); - return $this->response($this->authenticationProcessor->execute($request)); + $this->createsSeasonalSegment->execute($seasonalSegmentObject); + $this->assignSegmentProcessor->execute($company, $segment->id); + } + + if(config('perfexcrm.is_enabled') == 'true'){ + $createLeadPerfexCRMObject = new CreateLeadPerfexCRMObject( + $request->input('name'), + $request->input('email'), + $request->input('phone'), + $request->input('type') === CompanyType::COMPANY_BUSINESS ? $request->input('company_name') : $request->input('name'), + $company->reference + ); + CreatePerfexCRMCustomer::dispatch($createLeadPerfexCRMObject); + } + + $this->generateEmailVerificationAttemptProcessor->execute($user); + + $this->newCustomerToVoucherifyProcessor->execute($company->id, $user, true); + + $this->createVoucherProcessor->execute($user, 'WELCOME50%OFF'); + + return $this->response($this->authenticationProcessor->execute($request, false)); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Accounts/ControllersLogic/FetchUserByEmailLogic.php b/app/Classes/Modules/Accounts/ControllersLogic/FetchUserByEmailLogic.php new file mode 100644 index 00000000..5db3fab4 --- /dev/null +++ b/app/Classes/Modules/Accounts/ControllersLogic/FetchUserByEmailLogic.php @@ -0,0 +1,61 @@ + 'Retrieved User', + 'message' => 'You have successfully retrieved a User by email' + ]; + } + + /** @var CanFetchUser */ + private $canFetchUser; + + /** @var FetchesUser */ + private $fetchesUser; + + /** + * FetchUserByEmailLogic constructor. + * @param CanFetchUser $canFetchUser + * @param FetchesUser $fetchesUser + */ + public function __construct(CanFetchUser $canFetchUser, FetchesUser $fetchesCompany) + { + $this->canFetchUser = $canFetchUser; + $this->fetchesUser = $fetchesCompany; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function logic(Request $request) : JsonResponse + { + $this->canFetchUser->passes(); + + $query = $this->fetchesUser->execute(['email' => $request->route('email')]); + + return $this->resourceResponse(new UserCompanyResource($query)); + + } + +} diff --git a/app/Classes/Modules/Accounts/ControllersLogic/UpdateUserLogic.php b/app/Classes/Modules/Accounts/ControllersLogic/UpdateUserLogic.php index 7837c75e..6e252883 100644 --- a/app/Classes/Modules/Accounts/ControllersLogic/UpdateUserLogic.php +++ b/app/Classes/Modules/Accounts/ControllersLogic/UpdateUserLogic.php @@ -4,7 +4,7 @@ namespace App\Classes\Modules\Accounts\ControllersLogic; use App\Classes\General\Abstracts\AbstractControllerLogic; -use App\Classes\Modules\Accounts\Services\UpdatesUser; +use App\Classes\Modules\Accounts\Services\UpdatesUserFullName; use App\Classes\Modules\Accounts\Services\FetchesUser; use App\Classes\Modules\Accounts\Standards\Rules\CanUpdateUser; use App\Classes\Modules\Accounts\DataTransferObjects\FullNameObject; @@ -29,8 +29,8 @@ class UpdateUserLogic extends AbstractControllerLogic /** @var CanUpdateUser */ private $canUpdateUser; - /** @var UpdatesUser */ - private $updatesUser; + /** @var UpdatesUserFullName */ + private $updatesUserFullName; /** @var FetchesUser */ private $fetchesUser; @@ -41,10 +41,10 @@ class UpdateUserLogic extends AbstractControllerLogic * @param UpdatesUser $updatesUser * @param FetchesUser $fetchesUSer */ - public function __construct(CanUpdateUser $canUpdateUser, UpdatesUser $updatesUser, FetchesUser $fetchesUser) + public function __construct(CanUpdateUser $canUpdateUser, UpdatesUserFullName $updatesUserFullName, FetchesUser $fetchesUser) { $this->canUpdateUser = $canUpdateUser; - $this->updatesUser = $updatesUser; + $this->updatesUserFullName = $updatesUserFullName; $this->fetchesUser = $fetchesUser; } @@ -64,7 +64,7 @@ class UpdateUserLogic extends AbstractControllerLogic $query = $this->fetchesUser->execute(['id' => $request->route('id')]); - $query = $this->updatesUser->execute($query, $object); + $query = $this->updatesUserFullName->execute($query, $object); return $this->resourceResponse(new UserResource($query)); diff --git a/app/Classes/Modules/Accounts/ControllersLogic/UpdateUserRoleLogic.php b/app/Classes/Modules/Accounts/ControllersLogic/UpdateUserRoleLogic.php new file mode 100644 index 00000000..a1309a67 --- /dev/null +++ b/app/Classes/Modules/Accounts/ControllersLogic/UpdateUserRoleLogic.php @@ -0,0 +1,78 @@ + 'Updated Admin Role', + 'message' => 'You have successfully updated the Admin Role' + ]; + } + + /** @var CanUpdateUser */ + private $canUpdateUser; + + /** @var UpdatesUser */ + private $updatesUser; + + /** @var FetchesUser */ + private $fetchesUser; + + /** + * UpdateAddressLogic constructor. + * @param CanUpdateUser $canUpdateUser + * @param UpdatesUser $updatesUser + * @param FetchesUser $fetchesUSer + */ + public function __construct(CanUpdateUser $canUpdateUser, UpdatesUser $updatesUser, FetchesUser $fetchesUser) + { + $this->canUpdateUser = $canUpdateUser; + $this->updatesUser = $updatesUser; + $this->fetchesUser = $fetchesUser; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws ErrorException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->fetchesUser->execute(['id' => $request->route('id')]); + + $object = new UserObject( + $query->name, + $query->email, + $query->password, + $query->password, + $request->input('type'), + $query->status, + ); + + $this->canUpdateUser->passes($object); + + $query = $this->updatesUser->execute($query, $object); + + return $this->resourceResponse(new UserResource($query)); + } + +} \ No newline at end of file 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/Accounts/Services/GeneratesAuthenticationToken.php b/app/Classes/Modules/Accounts/Services/GeneratesAuthenticationToken.php index 72296b97..2116895c 100644 --- a/app/Classes/Modules/Accounts/Services/GeneratesAuthenticationToken.php +++ b/app/Classes/Modules/Accounts/Services/GeneratesAuthenticationToken.php @@ -65,8 +65,12 @@ class GeneratesAuthenticationToken { ]; if($user->type === RoleTypes::USER) { + + /** @var Company $companyModule */ $claims = array_merge($claims, [ - 'company_id' => $company->id + 'company_id' => $company->id, + 'company_name' => $company->name, + 'company_marking' => $company->reference, ]); } diff --git a/app/Classes/Modules/Accounts/Services/UpdatesUser.php b/app/Classes/Modules/Accounts/Services/UpdatesUser.php index f872b234..f7c6cfab 100644 --- a/app/Classes/Modules/Accounts/Services/UpdatesUser.php +++ b/app/Classes/Modules/Accounts/Services/UpdatesUser.php @@ -3,7 +3,7 @@ namespace App\Classes\Modules\Accounts\Services; use App\Classes\General\Eloquent\AbstractUpdateRecord; -use App\Classes\Modules\Accounts\DataTransferObjects\FullNameObject; +use App\Classes\Modules\Accounts\DataTransferObjects\UserObject; use App\Models\User; class UpdatesUser extends AbstractUpdateRecord @@ -15,9 +15,10 @@ class UpdatesUser extends AbstractUpdateRecord * @return \Illuminate\Database\Eloquent\Model * @throws \App\Classes\Exceptions\MalformedRequestException */ - public function execute(User $model, FullNameObject $object) { + public function execute(User $model, UserObject $object) { $model->name = $object->getName(); + $model->type = $object->getType(); return $this->handler($model); } diff --git a/app/Classes/Modules/Accounts/Services/UpdatesUserFullName.php b/app/Classes/Modules/Accounts/Services/UpdatesUserFullName.php new file mode 100644 index 00000000..9836e695 --- /dev/null +++ b/app/Classes/Modules/Accounts/Services/UpdatesUserFullName.php @@ -0,0 +1,24 @@ +name = $object->getName(); + return $this->handler($model); + + } +} \ 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/Announcements/ControllersLogic/CreateAnnouncementLogic.php b/app/Classes/Modules/Announcements/ControllersLogic/CreateAnnouncementLogic.php index 9c740601..319931bf 100644 --- a/app/Classes/Modules/Announcements/ControllersLogic/CreateAnnouncementLogic.php +++ b/app/Classes/Modules/Announcements/ControllersLogic/CreateAnnouncementLogic.php @@ -11,7 +11,7 @@ use App\Classes\Modules\Announcements\Services\CreatesAnnouncement; use App\Classes\Modules\Announcements\Processors\AssignAnnouncementToSegmentProcessor; use App\Http\Resources\AnnouncementResource; - +use Carbon\Carbon; use ErrorException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -67,8 +67,8 @@ class CreateAnnouncementLogic extends AbstractControllerLogic $annoucement_object = new AnnouncementObject( $request->input('title'), $request->input('description'), - $request->input('starting_on'), - $request->input('ending_on') + Carbon::createFromFormat('d-m-Y', $request->input('starting_on'))->format('Y-m-d H:i:s'), + Carbon::createFromFormat('d-m-Y', $request->input('ending_on'))->format('Y-m-d H:i:s') ); $this->canCreateAnnouncement->passes($annoucement_object); diff --git a/app/Classes/Modules/Announcements/ControllersLogic/UpdateAnnouncementLogic.php b/app/Classes/Modules/Announcements/ControllersLogic/UpdateAnnouncementLogic.php index 4fac4144..fc1f4359 100644 --- a/app/Classes/Modules/Announcements/ControllersLogic/UpdateAnnouncementLogic.php +++ b/app/Classes/Modules/Announcements/ControllersLogic/UpdateAnnouncementLogic.php @@ -10,7 +10,7 @@ use App\Classes\Modules\Announcements\Services\FetchesAnnouncement; use App\Classes\Modules\Announcements\Services\UpdatesAnnouncement; use App\Http\Resources\AnnouncementResource; - +use Carbon\Carbon; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -66,8 +66,8 @@ class UpdateAnnouncementLogic extends AbstractControllerLogic $annoucement_object = new AnnouncementObject( $request->input('title'), $request->input('description'), - $request->input('starting_on'), - $request->input('ending_on') + Carbon::createFromFormat('d-m-Y', $request->input('starting_on'))->format('Y-m-d H:i:s'), + Carbon::createFromFormat('d-m-Y', $request->input('ending_on'))->format('Y-m-d H:i:s') ); $this->canUpdateAnnouncement->passes($annoucement_object); diff --git a/app/Classes/Modules/Banks/ControllersLogic/CreateBankLogic.php b/app/Classes/Modules/Banks/ControllersLogic/CreateBankLogic.php index ff5b16c2..bb9a3468 100644 --- a/app/Classes/Modules/Banks/ControllersLogic/CreateBankLogic.php +++ b/app/Classes/Modules/Banks/ControllersLogic/CreateBankLogic.php @@ -6,8 +6,12 @@ use App\Classes\General\Abstracts\AbstractControllerLogic; 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; @@ -32,19 +36,37 @@ class CreateBankLogic extends AbstractControllerLogic /** @var CreatesBank */ private $createsBank; + /** @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 + CreatesBank $createsBank, + CreatesBankLog $createsBankLog, + FetchesCompany $fetchesCompany, + CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor ) { $this->canCreateBank = $canCreateBank; $this->createsBank = $createsBank; + $this->createsBankLog = $createsBankLog; + $this->fetchesCompany = $fetchesCompany; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; } /** @@ -56,7 +78,6 @@ class CreateBankLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { - $bank_object = new BankObject($request->input('company_id'), $request->input('account_type'), $request->input('bank_name'), $request->input('holder_name'), $request->input('account_no'), $request->input('bank_branch'), $request->input('swift'), $request->input('snap'), @@ -66,7 +87,14 @@ class CreateBankLogic extends AbstractControllerLogic $bank = $this->createsBank->execute($bank_object); +// $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/Banks/ControllersLogic/DeleteBankLogic.php b/app/Classes/Modules/Banks/ControllersLogic/DeleteBankLogic.php index 24975379..37c80843 100644 --- a/app/Classes/Modules/Banks/ControllersLogic/DeleteBankLogic.php +++ b/app/Classes/Modules/Banks/ControllersLogic/DeleteBankLogic.php @@ -7,6 +7,7 @@ use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Banks\Services\FetchesBank; use App\Classes\Modules\Banks\Standards\Rules\CanDeleteBank; use App\Classes\Modules\Banks\Services\DeletesBank; +use App\Classes\Modules\Banks\Services\CreatesBankLog; use App\Http\Resources\BankResource; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -33,22 +34,27 @@ class DeleteBankLogic extends AbstractControllerLogic /** @var FetchesBank */ private $fetchesBank; + /** @var CreatesBankLog */ + private $createsBankLog; /** * DeleteBankLogic constructor. * @param CanDeleteBank $canDeleteBank * @param DeletesBank $deletesBank * @param FetchesBank $fetchesBank + * @param CreatesBankLog $createsBankLog */ public function __construct( CanDeleteBank $canDeleteBank, DeletesBank $deletesBank, - FetchesBank $fetchesBank + FetchesBank $fetchesBank, + CreatesBankLog $createsBankLog ) { $this->canDeleteBank = $canDeleteBank; $this->deletesBank = $deletesBank; $this->fetchesBank = $fetchesBank; + $this->createsBankLog = $createsBankLog; } /** @@ -69,7 +75,9 @@ class DeleteBankLogic extends AbstractControllerLogic throw new RequestValidationException('You can\'t delete bank account when it set to default'); } - $this->deletesBank->execute($bank); + $bank = $this->deletesBank->execute($bank); + +// $bankLog = $this->createsBankLog->execute($bank); return $this->response([]); } diff --git a/app/Classes/Modules/Banks/ControllersLogic/UpdateBankLogic.php b/app/Classes/Modules/Banks/ControllersLogic/UpdateBankLogic.php index 9cb8fdd2..8509ed6b 100644 --- a/app/Classes/Modules/Banks/ControllersLogic/UpdateBankLogic.php +++ b/app/Classes/Modules/Banks/ControllersLogic/UpdateBankLogic.php @@ -10,6 +10,8 @@ use App\Classes\Modules\Banks\Services\FetchesBank; use App\Classes\Modules\Banks\Standards\Rules\CanUpdateBank; use App\Classes\Modules\Banks\Services\UpdatesBank; +use App\Classes\Modules\Banks\Services\CreatesBankLog; + use App\Classes\Modules\Banks\DataTransferObjects\BankObject; use ErrorException; @@ -39,21 +41,27 @@ class UpdateBankLogic extends AbstractControllerLogic /** @var FetchesBank */ private $fetchesBank; + /** @var CreatesBankLog */ + private $createsBankLog; + /** * UpdateBankLogic constructor. * @param CanUpdateBank $canUpdateBank * @param UpdatesBank $updatesBank * @param FetchesBank $fetchesBank + * @param CreatesBankLog $createsBankLog */ public function __construct( CanUpdateBank $canUpdateBank, UpdatesBank $updatesBank, - FetchesBank $fetchesBank + FetchesBank $fetchesBank, + CreatesBankLog $createsBankLog ) { $this->canUpdateBank = $canUpdateBank; $this->updatesBank = $updatesBank; $this->fetchesBank = $fetchesBank; + $this->createsBankLog = $createsBankLog; } /** @@ -84,6 +92,8 @@ class UpdateBankLogic extends AbstractControllerLogic $bank_query = $this->updatesBank->execute($bank, $bankObject); +// $bankLog = $this->createsBankLog->execute($bank_query); + return $this->resourceResponse(new BankResource($bank_query)); } diff --git a/app/Classes/Modules/Banks/ControllersLogic/UpdateBankStatusLogic.php b/app/Classes/Modules/Banks/ControllersLogic/UpdateBankStatusLogic.php index 09ee2796..c2522954 100644 --- a/app/Classes/Modules/Banks/ControllersLogic/UpdateBankStatusLogic.php +++ b/app/Classes/Modules/Banks/ControllersLogic/UpdateBankStatusLogic.php @@ -6,6 +6,7 @@ use App\Http\Resources\BankResource; use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Banks\Services\FetchesBank; use App\Classes\Modules\Banks\Services\UpdatesBankStatus; +use App\Classes\Modules\Banks\Services\CreatesBankLog; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -28,18 +29,24 @@ class UpdateBankStatusLogic extends AbstractControllerLogic /** @var UpdatesBankStatus */ private $updatesBankStatus; + /** @var CreatesBankLog */ + private $createsBankLog; + /** * UpdateBankStatusLogic constructor. * @param FetchesBank $fetchesBank * @param UpdatesBankStatus $updatesBankStatus + * @param CreatesBankLog $createsBankLog */ public function __construct( FetchesBank $fetchesBank, - UpdatesBankStatus $updatesBankStatus + UpdatesBankStatus $updatesBankStatus, + CreatesBankLog $createsBankLog ) { $this->fetchesBank = $fetchesBank; $this->updatesBankStatus = $updatesBankStatus; + $this->createsBankLog = $createsBankLog; } /** @@ -55,6 +62,8 @@ class UpdateBankStatusLogic extends AbstractControllerLogic $bank_query = $this->updatesBankStatus->execute($bank, $request->input('status')); +// $bankLog = $this->createsBankLog->execute($bank_query); + return $this->resourceResponse(new BankResource($bank_query)); } diff --git a/app/Classes/Modules/Banks/Services/CreatesBankLog.php b/app/Classes/Modules/Banks/Services/CreatesBankLog.php new file mode 100644 index 00000000..9d7877e1 --- /dev/null +++ b/app/Classes/Modules/Banks/Services/CreatesBankLog.php @@ -0,0 +1,32 @@ +bank_id = $object->id; + $model->company_id = $object->company_id; + $model->reference = $object->reference; + $model->bank_name = $object->bank_name; + $model->holder_name = $object->holder_name; + $model->account_no = $object->account_no; + $model->bank_branch = $object->bank_branch; + $model->swift = $object->swift; + $model->snap = $object->snap; + $model->type = $object->type; + $model->country_id = $object->country_id; + return $this->handler($model); + + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php b/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php index 2230343f..186ef3b6 100644 --- a/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php +++ b/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php @@ -4,6 +4,7 @@ namespace App\Classes\Modules\Billplzs\ControllersLogic; use App\Classes\Modules\Wallets\DataTransferObjects\WalletObject; use App\Classes\Modules\Wallets\Services\UpdatesWallet; +use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor; use App\Classes\Exceptions\ResourceNotFoundException; use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance; @@ -16,6 +17,7 @@ use Illuminate\Http\JsonResponse; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\Modules\Billplzs\Services\GetBillplzBill; use App\Classes\Modules\Billplzs\DataTransferObjects\BillplzXSignatureObject; use App\Classes\General\Abstracts\AbstractControllerLogic; @@ -23,6 +25,7 @@ use App\Classes\Modules\Transactions\Services\FetchesTransaction; use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; +use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance; class CallbackBillplzLogic { @@ -40,19 +43,28 @@ class CallbackBillplzLogic /** @var UpdatesWalletBalance */ private $updatesWalletBalance; + /** @var CreateCashBackTransactionProcessor */ + private $createCashBackTransactionProcessor; + + /** @var RecalculatesWalletBalance */ + private $recalculatesWalletBalance; + /** * CallbackBillplzLogic constructor. * @param GetBillplzBill $getBillplzBill * @param FetchesTransaction $fetchesTransaction * @param UpdatesTransactionStatus $updatesTransactionStatus * @param UpdatesWalletBalance $updatesWalletBalance + * @param RecalculatesWalletBalance $recalculatesWalletBalance */ - public function __construct(GetBillplzBill $getBillplzBill, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, UpdatesWalletBalance $updatesWalletBalance) + public function __construct(GetBillplzBill $getBillplzBill, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, UpdatesWalletBalance $updatesWalletBalance, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, RecalculatesWalletBalance $recalculatesWalletBalance) { $this->getBillplzBill = $getBillplzBill; $this->fetchesTransaction = $fetchesTransaction; $this->updatesTransactionStatus = $updatesTransactionStatus; $this->updatesWalletBalance = $updatesWalletBalance; + $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; + $this->recalculatesWalletBalance = $recalculatesWalletBalance; } @@ -80,26 +92,29 @@ class CallbackBillplzLogic $status = ApprovalStatus::APPROVED; } + if($billPlz->state === 'due') { $status = $billplzXSignatureObject->getStatus() === 'failed' ? ApprovalStatus::REJECTED : ApprovalStatus::PENDING_VERIFICATION; } - if($transaction->status !== ApprovalStatus::COMPLETED){ - - if($transaction->owner instanceof Wallet && $transaction->status !== ApprovalStatus::APPROVED && $status === ApprovalStatus::APPROVED) { - $this->updatesWalletBalance->execute($transaction->owner, $transaction->amount); - } + if($transaction->status !== ApprovalStatus::COMPLETED && $transaction->status !== ApprovalStatus::APPROVED){ $this->updatesTransactionStatus->execute($transaction, $status); - } + if($transaction->owner instanceof Wallet) { + $this->updatesWalletBalance->execute($transaction->owner, $transaction->amount); + } + +// if ($transaction->type == TransactionType::PAYMENT) { +// $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction); +// } $token = Auth::fromUser(User::find(1)); $request->headers->set('Authorization', 'Bearer '.$token); - $marking = $transaction->owner instanceof Booking ? $transaction->booking->marking : $transaction->owner->owner->bookings()->orderBy('id', 'DESC')->first()->marking; + $marking = $transaction->owner instanceof Booking ? $transaction->booking->marking : (count($transaction->owner->owner->bookings()->get())? $transaction->owner->owner->bookings()->orderBy('id', 'DESC')->first()->marking: null); - return $request->method() === 'POST' ? true : view('pages.payments_redirect', ['marking' => $marking, 'transaction' => $transaction, 'status' => $status]); + return $request->method() === 'POST' ? true : view('pages.payments_redirect', ['marking' => $marking ?? null, 'transaction' => $transaction, 'status' => $status]); } } \ No newline at end of file diff --git a/app/Classes/Modules/Billplzs/Services/CreatesBillplzBill.php b/app/Classes/Modules/Billplzs/Services/CreatesBillplzBill.php index 5b331fca..a2947e82 100644 --- a/app/Classes/Modules/Billplzs/Services/CreatesBillplzBill.php +++ b/app/Classes/Modules/Billplzs/Services/CreatesBillplzBill.php @@ -4,6 +4,8 @@ namespace App\Classes\Modules\Billplzs\Services; use Illuminate\Support\Facades\Http; use App\Classes\Exceptions\MalformedRequestException; +use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Log; class CreatesBillplzBill { @@ -25,7 +27,7 @@ class CreatesBillplzBill $response = Http::withBasicAuth(config('billplz.api_key').':', '')->post(config('billplz.base_url').'/api/v3/bills', [ 'collection_id' => $wallet ? config('billplz.wallet_collection_id') : config('billplz.collection_id'), 'name' => $name, - 'email' => $email, + 'email' => App::environment('production') ? $email : 'development@cief-malaysia.com', 'description' => $description, 'amount' => $this->finalizeAmount($amount), 'redirect_url' => route('online_payment.redirect'), @@ -43,6 +45,7 @@ class CreatesBillplzBill return (object) $data; }else{ + Log::error($response); return null; } }catch(\Exception $exception){ diff --git a/app/Classes/Modules/Bookings/ControllersLogic/ApprovePaymentVerificationLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/ApprovePaymentVerificationLogic.php index b0a01562..c20a44d2 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/ApprovePaymentVerificationLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/ApprovePaymentVerificationLogic.php @@ -2,16 +2,16 @@ namespace App\Classes\Modules\Bookings\ControllersLogic; - use App\Classes\General\Abstracts\AbstractControllerLogic; - use App\Classes\Modules\Documents\Services\ApprovesDocument; use App\Classes\Modules\Documents\Services\FetchesDocument; use App\Classes\Modules\Documents\Services\RejectsDocument; use App\Classes\Modules\Transactions\Services\FetchesTransaction; use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; +use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor; + use App\Classes\ValueObjects\Constants\ApprovalStatus; use Illuminate\Http\JsonResponse; @@ -26,14 +26,15 @@ class ApprovePaymentVerificationLogic extends AbstractControllerLogic * @param ApprovesDocument $approvesDocument * @param RejectsDocument $rejectsDocument * @param FetchesDocument $fetchesDocument - * @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor + * @param CreateCashBackTransactionProcessor $createCashBackTransactionProcessor */ - public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, ApprovesDocument $approvesDocument, RejectsDocument $rejectsDocument, FetchesDocument $fetchesDocument) + public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, ApprovesDocument $approvesDocument, RejectsDocument $rejectsDocument, FetchesDocument $fetchesDocument, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor) { $this->fetchesTransaction = $fetchesTransaction; $this->updatesTransactionStatus = $updatesTransactionStatus; $this->approvesDocument = $approvesDocument; $this->rejectsDocument = $rejectsDocument; + $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; } /** @@ -58,6 +59,9 @@ class ApprovePaymentVerificationLogic extends AbstractControllerLogic /** @var RejectsDocument */ private $rejectsDocument; + /** @var CreateCashBackTransactionProcessor */ + private $createCashBackTransactionProcessor; + /** * @param Request $request * @return JsonResponse @@ -65,7 +69,6 @@ class ApprovePaymentVerificationLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { - $status = $request->route('status'); $transaction = $this->fetchesTransaction->execute(['id' => $request->route('payment_id')]); @@ -74,7 +77,7 @@ class ApprovePaymentVerificationLogic extends AbstractControllerLogic $this->updatesTransactionStatus->execute($transaction, $status === 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED); - // $this->createInvoiceTransactionProcessor->execute($transaction->booking); +// $this->createCashBackTransactionProcessor->execute($transaction); return $this->response([]); } diff --git a/app/Classes/Modules/Bookings/ControllersLogic/AutoPurchaseOrderFillLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/AutoPurchaseOrderFillLogic.php index 73751cfb..8eb47681 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/AutoPurchaseOrderFillLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/AutoPurchaseOrderFillLogic.php @@ -64,8 +64,8 @@ class AutoPurchaseOrderFillLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { - $bookings = Booking::where(function($query){ - return $query->whereMonth('created_at', 11)->orWhereMonth('created_at', 12); + $bookings = Booking::where('service_id', '!=', 4)->where(function($query){ + return $query->whereMonth('created_at', '=', 03)->whereYear('created_at', 2023); })->whereDoesntHave('transactions', function($q){ $q->where('type', TransactionType::PURCHASE_ORDER); $q->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED]); @@ -111,4 +111,4 @@ class AutoPurchaseOrderFillLogic extends AbstractControllerLogic return $this->response([]); } -} \ 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 e4fa3666..61f78dcb 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php @@ -13,8 +13,12 @@ use App\Classes\Modules\Bookings\Services\CreatesBooking; 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; use ErrorException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -44,19 +48,30 @@ class CreateBookingLogic extends AbstractControllerLogic /** @var FetchesCompany */ private $fetchesCompany; + /** @var BookingToPerfexCRMProcessor */ + private $bookingToPerfexCRMProcessor; + + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; + + /** * CreateBookingLogic constructor. * @param CanCreateBooking $canCreateBooking * @param CreatesBooking $createsBooking * @param GeneratesBookingMarking $generatesBookingMarking * @param FetchesCompany $fetchesCompany + * @param BookingToPerfexCRMProcessor $bookingToPerfexCRMProcessor + * @param CheckMilestoneForRewardProcessor $checkMilestonesForRewardProcessor */ - public function __construct(CanCreateBooking $canCreateBooking, CreatesBooking $createsBooking, GeneratesBookingMarking $generatesBookingMarking, FetchesCompany $fetchesCompany) + 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; } @@ -75,9 +90,18 @@ class CreateBookingLogic extends AbstractControllerLogic $this->canCreateBooking->passes($object); + /** @var Booking $booking */ $booking = $this->createsBooking->execute($company, $object); + if(config('perfexcrm.is_enabled') == 'true'){ + $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)); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php index ecb6a87c..c429d91d 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php @@ -8,10 +8,8 @@ use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding; use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation; use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit; -use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject; use App\Classes\Modules\Transactions\Services\CreatesTransaction; -use App\Classes\Modules\Transactions\Services\UpdatesTransaction; use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber; use App\Classes\Modules\Billplzs\Services\CreatesBillplzBill; use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; @@ -19,13 +17,19 @@ 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; +use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance; class CreateBookingPaymentLogic extends AbstractControllerLogic { @@ -64,6 +68,15 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic /** @var UpdatesTransactionStatus */ private $updatesTransactionStatus; + /** @var CreateCashBackTransactionProcessor */ + private $createCashBackTransactionProcessor; + + /** @var RecalculatesWalletBalance */ + private $recalculatesWalletBalance; + + /** @var BookingToVoucherifyProcessor */ + private $bookingToVoucherifyProcessor; + /** * CreateBookingPaymentLogic constructor. * @param FetchesBookingQuotation $fetchBookingQuotation @@ -74,8 +87,11 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic * @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) + 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; @@ -85,6 +101,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic $this->createsBillplzBill = $createsBillplzBill; $this->updatesWalletBalance = $updatesWalletBalance; $this->updatesTransactionStatus = $updatesTransactionStatus; + $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; + $this->recalculatesWalletBalance = $recalculatesWalletBalance; + $this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor; } /** @@ -94,6 +113,8 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { + $voucherCode = $request->input('voucher_code'); + $booking = Booking::find($request->route('id')); $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $request->input('amount'))), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')]); @@ -102,7 +123,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic if($conversionObject->getAmount() > round($outstanding, 2)) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.'); - $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject); + $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject, $voucherCode); $paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company); @@ -113,7 +134,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic $amount = $configurations->getTotal(); if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::PAYMENT_GATEWAY){ - $billPlzBill = $this->createsBillplzBill->execute($request->user()->name, $request->user()->email, 'This payment is made for transfer ref. '.$booking->marking, $configurations->getTotal(), $billNumber, $request->input('bank_code')); + $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; } @@ -121,17 +142,17 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic /** @var Wallet $wallet */ $wallet = $booking->company->wallets()->first(); - if(round($wallet->amount) < round($amount, 2)){ + 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, [], ''); - $this->createsTransaction->execute($wallet, $transaction_object); + $transaction = $this->createsTransaction->execute($wallet, $transaction_object); $paymentReference = $billNumber; - $this->updatesWalletBalance->execute($wallet, ($amount * -1)); - + $walletBalance = $this->recalculatesWalletBalance->execute($wallet); + $this->updatesWalletBalance->execute($wallet, $walletBalance); } $billNumber = $this->generatesTransactionBillNumber->execute('PYMT-'); @@ -144,6 +165,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic /** @var Transaction $transaction */ $transaction = $this->createsTransaction->execute($booking, $object); +// $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction); + + $this->bookingToVoucherifyProcessor->execute($booking->company->employees()->first(), $transaction, $booking->company->id, $configurations->getSubTotal(), $configurations->getVoucherDiscountAmount(), $voucherCode); if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::WALLET){ $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED); @@ -153,4 +177,4 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php index 74e92fa6..d31ee2ce 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php @@ -84,12 +84,14 @@ class CreateBookingRefundLogic extends AbstractControllerLogic $billNumber = $this->generatesTransactionBillNumber->execute('RFD-'); - $refund = $transaction->transactions()->refunds()->sum('amount'); if($refund + $request->input('amount') > $transaction->original_amount) throw new MalformedRequestException('Your refund must not be greater than '. $transaction->original_amount .'.'); - $transactionRefundCalculationObject = new TransactionRefundCalculationObject($booking, $transaction, $request->input('amount')); + $amount = $transaction->booking->fix_currency_id == 1 ? $request->input('amount') : $request->input('amount') / $transaction->currency_rate; + + + $transactionRefundCalculationObject = new TransactionRefundCalculationObject($booking, $transaction, $amount); $transactionRefundCalculationObject->init(); $object = new TransactionObject($billNumber, TransactionType::REFUND, 1, $booking->company->id, diff --git a/app/Classes/Modules/Bookings/ControllersLogic/DeletePurchaseOrderPdfLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/DeletePurchaseOrderPdfLogic.php new file mode 100644 index 00000000..5bc44d34 --- /dev/null +++ b/app/Classes/Modules/Bookings/ControllersLogic/DeletePurchaseOrderPdfLogic.php @@ -0,0 +1,69 @@ + 'Purchase Order PDF Deleted', + 'message' => 'You have successfully deleted the Purchase order PDF' + ]; + } + + /** @var FetchesBooking */ + private $fetchesBooking; + + /** @var DeletesDocument */ + private $deletesDocument; + + /** @var CanDeleteDocument */ + private $canDeleteDocument; + + + /** + * DeletePurchaseOrderPdfLogic constructor. + * @param FetchesBooking $fetchesBooking + * @param DeletesDocument $deletesDocument + * @param CanDeleteDocument $canDeleteDocument + */ + public function __construct(fetchesBooking $fetchesBooking, DeletesDocument $deletesDocument, CanDeleteDocument $canDeleteDocument) + { + $this->fetchesBooking = $fetchesBooking; + $this->deletesDocument = $deletesDocument; + $this->canDeleteDocument = $canDeleteDocument; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $booking = $this->fetchesBooking->execute(['id' => $request->route('id')]); + + $document = $booking->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->first(); + + $this->canDeleteDocument->passes(); + + $this->deletesDocument->execute($document); + + return $this->response([]); + } + +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/DownloadBookingDocumentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/DownloadBookingDocumentLogic.php index d8fdb0df..ff9427fe 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/DownloadBookingDocumentLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/DownloadBookingDocumentLogic.php @@ -14,6 +14,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Storage; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\ValueObjects\Constants\DocumentType; class DownloadBookingDocumentLogic { @@ -26,9 +27,9 @@ class DownloadBookingDocumentLogic */ public function execute(Request $request) { - Auth::login(User::findOrFail(1)); - $zip_file = $request->input('type').'.zip'; + $document_type = str_replace(' ', '', $request->input('type')); + $zip_file = $document_type.'.zip'; $attachment = storage_path().'/app/documents/collections/' . $zip_file; $zip = new ZipArchive(); @@ -36,19 +37,42 @@ class DownloadBookingDocumentLogic $bookings = Booking::where('status', ApprovalStatus::COMPLETED) ->whereDate('created_at', '>=', Carbon::parse($request->input('startDate'))) - ->whereDate('created_at', '<=', Carbon::parse($request->input('endDate')))->whereHas('transactions', function ($query) use ($request){ + ->whereDate('created_at', '<=', Carbon::parse($request->input('endDate'))) + ->whereHas('transactions', function ($query) use ($request){ return $query->where('type', TransactionType::PAYMENT)->whereHas('transactions', function ($query) use ($request){ return $query->where('type', TransactionType::BILL)->where('issuer', $request->input('supplier')); }); })->get(); - if (!count($bookings)) throw new MalformedRequestException('No available file to download'); - foreach ($bookings as $booking) { - $file = $booking->documents()->where('document_type', $request->input('type'))->first()->files()->first(); - $zip->addFile(Storage::disk('documents')->path($file->file->file_info->original->file), $booking->created_at->format('d_m_Y') . '_' . $booking->marking . '.pdf'); + if (!count($bookings)) { + return response()->json(['no file to download']); } + foreach ($bookings as $booking) { + + if ($document_type == 'INVOICEPODO' || $document_type == 'INVOICEPODOSDO') { + $invoice_file = $booking->documents()->where('document_type', DocumentType::INVOICE)->first()->files()->first(); + $purchase_file = $booking->documents()->where('document_type', DocumentType::PURCHASE_ORDER)->first()->files()->first(); + $deliver_file = $booking->documents()->where('document_type', DocumentType::DELIVER_ORDER)->first()->files()->first(); + if ($document_type == 'INVOICEPODOSDO') { + $supplier_deliver_order_file = $booking->documents()->where('document_type', DocumentType::SUPPLIER_DELIVER_ORDER)->first()->files()->first(); + } + + $zip->addFile(Storage::disk('documents')->path($invoice_file->file->file_info->original->file), 'invoice-' . $booking->created_at->format('d_m_Y') . '_' . $booking->marking . '.pdf'); + + $zip->addFile(Storage::disk('documents')->path($purchase_file->file->file_info->original->file), 'purchase-order-' . $booking->created_at->format('d_m_Y') . '_' . $booking->marking . '.pdf'); + + $zip->addFile(Storage::disk('documents')->path($deliver_file->file->file_info->original->file), 'deliver-' . $booking->created_at->format('d_m_Y') . '_' . $booking->marking . '.pdf'); + + $zip->addFile(Storage::disk('documents')->path($supplier_deliver_order_file->file->file_info->original->file), 'supplier-deliver-order-' . $booking->created_at->format('d_m_Y') . '_' . $booking->marking . '.pdf'); + } + else { + $file = $booking->documents()->where('document_type', $document_type)->first()->files()->first(); + $zip->addFile(Storage::disk('documents')->path($file->file->file_info->original->file), $booking->created_at->format('d_m_Y') . '_' . $booking->marking . '.pdf'); + } + + } $zip->close(); while (ob_get_level()) { ob_end_clean(); diff --git a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php index eaac0adc..8effdb2f 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php @@ -62,7 +62,6 @@ class FetchBookingPaymentQuotationLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { - $booking = Booking::find($request->route('id')); $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $request->input('amount'))), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')]); @@ -70,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 )]); @@ -79,4 +81,4 @@ class FetchBookingPaymentQuotationLogic extends AbstractControllerLogic } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/RegenerateInvoiceBookingLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/RegenerateInvoiceBookingLogic.php index 71ea7ba1..252412a2 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/RegenerateInvoiceBookingLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/RegenerateInvoiceBookingLogic.php @@ -10,6 +10,7 @@ use App\Classes\Modules\Transactions\Services\DeletesTransaction; use App\Classes\Modules\Documents\Services\DeletesDocument; use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessor; +use App\Classes\ValueObjects\Constants\DocumentType; use App\Http\Resources\BookingResource; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -57,7 +58,7 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic * @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor */ public function __construct( - CanFetchBooking $canFetchBooking, + CanFetchBooking $canFetchBooking, FetchesBooking $fetchesBooking, DeletesTransaction $deletesTransaction, UpdatesBookingStatus $updatesBookingStatus, @@ -86,7 +87,7 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic $this->canFetchBooking->passes(); $booking = $this->fetchesBooking->execute([ - 'id' => $request->route('id'), + 'id' => $request->route('id'), 'status' => ApprovalStatus::COMPLETED, 'with_transactions' => true] ); @@ -98,7 +99,7 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic $this->deletesTransaction->execute($row); } - $document = $booking->documents()->get(); + $document = $booking->documents()->whereIn('document_type', [DocumentType::PURCHASE_ORDER, DocumentType::INVOICE, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->get(); foreach ($document as $key => $row) { $this->deletesDocument->execute($row); } @@ -108,4 +109,4 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic return $this->resourceResponse(new BookingResource($booking)); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingOwnerLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingOwnerLogic.php new file mode 100644 index 00000000..90b3fb98 --- /dev/null +++ b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingOwnerLogic.php @@ -0,0 +1,87 @@ + 'Update Booking Owner', + 'message' => "You have successfully updated booking's owner" + ]; + } + + /** @var CanUpdateBooking */ + private $canUpdateBooking; + + /** @var UpdatesBookingOwner */ + private $updatesBookingOwner; + + /** @var FetchesBooking */ + private $fetchesBooking; + + /** @var CanFetchBooking */ + private $canFetchBooking; + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** + * UpdateBookingLogic constructor. + * @param CanUpdateBooking $canUpdateBooking + * @param UpdatesBookingOwner $updatesBookingOwner + * @param CanFetchBooking $canFetchBooking + * @param FetchesBooking $fetchesBooking + * @param FetchesCompany $fetchesCompany + */ + public function __construct( + CanUpdateBooking $canUpdateBooking, + UpdatesBookingOwner $updatesBookingOwner, + CanFetchBooking $canFetchBooking, + FetchesBooking $fetchesBooking, + FetchesCompany $fetchesCompany + ) { + $this->canUpdateBooking = $canUpdateBooking; + $this->updatesBookingOwner = $updatesBookingOwner; + $this->canFetchBooking = $canFetchBooking; + $this->fetchesBooking = $fetchesBooking; + $this->fetchesCompany = $fetchesCompany; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws ErrorException + */ + public function logic(Request $request): JsonResponse + { + $this->canFetchBooking->passes(); + + $booking = $this->fetchesBooking->execute([ + 'id' => $request->route('id'), + ]); + + $newCompanyId = Company::where('reference', $request->input('newMarking'))->first()->id; + + $this->updatesBookingOwner->execute($booking, $newCompanyId); + + return $this->resourceResponse(new BookingResource($booking)); + } +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/UploadPurchaseOrderLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/UploadPurchaseOrderLogic.php new file mode 100644 index 00000000..ef6124cf --- /dev/null +++ b/app/Classes/Modules/Bookings/ControllersLogic/UploadPurchaseOrderLogic.php @@ -0,0 +1,94 @@ + 'Purchase Order Uploaded', + 'message' => 'You have successfully uploaded the Purchase order' + ]; + } + + /** @var FetchesBooking */ + private $fetchesBooking; + + /** @var CreatesDocument */ + private $createsDocument; + + /** @var CreatesFiles */ + private $createsFile; + + /** @var CreatePurchaseOrderFor1688OrderProcessor */ + private $createPurchaseOrderFor1688OrderProcessor; + + + /** + * UploadPurchaseOrderLogic constructor. + * @param FetchesBooking $fetchesBooking + * @param CreatesDocument $createsDocument + * @param CreatesFiles $createsFile + * @param CreatePurchaseOrderFor1688OrderProcessor $createPurchaseOrderFor1688OrderProcessor + */ + public function __construct(FetchesBooking $fetchesBooking, CreatesDocument $createsDocument, CreatesFiles $createsFile, CreatePurchaseOrderFor1688OrderProcessor $createPurchaseOrderFor1688OrderProcessor) + { + $this->fetchesBooking = $fetchesBooking; + $this->createsDocument = $createsDocument; + $this->createsFile = $createsFile; + $this->createPurchaseOrderFor1688OrderProcessor = $createPurchaseOrderFor1688OrderProcessor; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + + $booking = $this->fetchesBooking->execute(['id' => $request->route('id')]); + + $object = new DocumentObject(DocumentType::ECOMMERCE_PURCHASE_ORDER, $request->input('files'), '', ApprovalStatus::APPROVED, '1688_purchase_orders'); + + /** @var Document $document */ + $document = $this->createsDocument->execute($booking, $object); + + $this->createsFile->execute($document, $object); + + if(!in_array($booking->company->id, [199, 510])){ + $this->createPurchaseOrderFor1688OrderProcessor->execute($booking); + } + + return $this->response([]); + } + +} 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/Processors/CreatePurchaseOrderFor1688OrderProcessor.php b/app/Classes/Modules/Bookings/Processors/CreatePurchaseOrderFor1688OrderProcessor.php new file mode 100644 index 00000000..fc7190cc --- /dev/null +++ b/app/Classes/Modules/Bookings/Processors/CreatePurchaseOrderFor1688OrderProcessor.php @@ -0,0 +1,96 @@ +generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor; + $this->convert1688PurchaseOrderToProductList = $convert1688PurchaseOrderToProductList; + $this->updatesTransactionStatus = $updatesTransactionStatus; + $this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor; + } + + + public function execute(Booking $booking) + { + $billNumber = $this->generatesTransactionBillNumber->execute('PO-'); + + $documents = $booking->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->get(); + $products = []; + foreach ($documents as $document) { + foreach ($document->files as $file) { + try { + $products = $this->convert1688PurchaseOrderToProductList->execute($file); + } catch (MalformedRequestException $exception) { + continue; + } + } + } + + if (empty($products)){ + $object = new TransactionObject($billNumber, TransactionType::PURCHASE_ORDER, $booking->company->id, 1, + 1, PaymentMethodType::CASH, + 0, 0, $booking->fix_currency_id, $booking->fix_currency_id, + 1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, []); + $this->createPurchaseOrderTransactionProcessor->execute($booking, $object); + return false; + } + + $total = collect($products)->sum(function($product){ + return $product['quantity'] * floatval(str_replace(',', '', $product['unit_price'])); + }); + + $object = new TransactionObject($billNumber, TransactionType::PURCHASE_ORDER, $booking->company->id, 1, + 1, PaymentMethodType::CASH, + $total, $total, $booking->fix_currency_id, $booking->fix_currency_id, + 1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, $products); + + $purchaseOrder = $this->createPurchaseOrderTransactionProcessor->execute($booking, $object); + + if($purchaseOrder->amount === $booking->fix_amount){ + $this->updatesTransactionStatus->execute($purchaseOrder, ApprovalStatus::APPROVED); + $this->createInvoiceTransactionProcessor->execute($booking); + } + + } + +} diff --git a/app/Classes/Modules/Bookings/Services/Convert1688PurchaseOrderToProductList.php b/app/Classes/Modules/Bookings/Services/Convert1688PurchaseOrderToProductList.php new file mode 100644 index 00000000..b956aa39 --- /dev/null +++ b/app/Classes/Modules/Bookings/Services/Convert1688PurchaseOrderToProductList.php @@ -0,0 +1,91 @@ +parseFile(Storage::disk('documents')->path($file->file->file_info->original->file)); + } catch (Exception $exception) { + throw new MalformedRequestException('Invalid file format.'); + } + + $text = $pdf->getText(); + if(str_contains($text, '订单详情单')) throw new MalformedRequestException('Can\'t process non english documents'); + + $tables = explode('Amount (yuan)', $text); + $footer = end($tables); + $footer = preg_split('(Shipping|运费)', $footer); + if(array_key_exists(1, $footer)){ + $footer = preg_split('/[\t\n]/', $footer[1]); + $shipping = (float) filter_var( $footer[0], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ); + $discount = array_filter($footer, function($var) { return preg_match("/(Discount|优惠)/", $var); }); + $discount = (float) filter_var(reset($discount), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION ); + } + + + array_shift($tables); + + foreach ($tables as $table){ + $products = preg_split('/(yuan\/|yuan \/|元\/|元 \/)/', str_replace("\r\n","",$table)); + + array_pop($products); + foreach ($products as $product){ + $product = preg_split('/[\t]/',$product); + + $adjuster = (count($product) - 7); + $descriptionIndex = 3 + $adjuster; + + $quantity = (int) str_replace("\n","",$product[$descriptionIndex + 2]); + $unitPrice = (float) str_replace("\n","",$product[$descriptionIndex + 3]); + + $productList[] = [ + 'stockCode' => '', + 'description' => str_replace("\n","",$product[$descriptionIndex]), + 'quantity' => $quantity, + 'unit_price' => $unitPrice, + 'total' => (float) str_replace("\n","",$product[$descriptionIndex + 3]) + ]; + } + } + + if($shipping > 0) { + $productList[] = [ + 'stockCode' => '', + 'description' => 'Shipping Fee', + 'quantity' => 1, + 'unit_price' => $shipping, + 'total' => $shipping + ]; + } + + if($discount > 0) { + $productList[] = [ + 'stockCode' => '', + 'description' => 'Discount', + 'quantity' => 1, + 'unit_price' => $discount * -1, + 'total' => $discount * -1 + ]; + } + + return $productList; + } +} 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/Bookings/Services/UpdatesBookingOwner.php b/app/Classes/Modules/Bookings/Services/UpdatesBookingOwner.php new file mode 100644 index 00000000..1fd094e4 --- /dev/null +++ b/app/Classes/Modules/Bookings/Services/UpdatesBookingOwner.php @@ -0,0 +1,22 @@ +company_id = $id; + return $this->handler($model); + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Companies/ControllersLogic/ApproveIdentificationDocumentLogic.php b/app/Classes/Modules/Companies/ControllersLogic/ApproveIdentificationDocumentLogic.php index d5e052a9..cf894bfd 100644 --- a/app/Classes/Modules/Companies/ControllersLogic/ApproveIdentificationDocumentLogic.php +++ b/app/Classes/Modules/Companies/ControllersLogic/ApproveIdentificationDocumentLogic.php @@ -13,6 +13,9 @@ use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Documents\Services\FetchesDocument; use App\Classes\Modules\Documents\Services\ApprovesDocument; +use App\Classes\Modules\Notifications\DataTransferObjects\NotificationObject; +Use App\Classes\Modules\Notifications\Processors\CreateNotificationProcessor; +use App\Classes\General\Interfaces\Notifiable; use App\Models\Document; use Illuminate\Http\JsonResponse; @@ -46,6 +49,9 @@ class ApproveIdentificationDocumentLogic extends AbstractControllerLogic /** @var UpdatesCompanyStatus */ private $updatesCompanyStatus; + /** @var CreateNotificationProcessor */ + private $createNotificationProcessor; + /** * ApproveIdentificationDocumentLogic constructor. * @param CanApproveDocument $canApproveDocument @@ -53,14 +59,16 @@ class ApproveIdentificationDocumentLogic extends AbstractControllerLogic * @param RejectsDocument $rejectsDocument * @param FetchesDocument $fetchesDocument * @param UpdatesCompanyStatus $updatesCompanyStatus + * @param CreateNotificationProcessor $createNotificationProcessor */ - public function __construct(CanApproveDocument $canApproveDocument, ApprovesDocument $approvesDocument, RejectsDocument $rejectsDocument, FetchesDocument $fetchesDocument, UpdatesCompanyStatus $updatesCompanyStatus) + public function __construct(CanApproveDocument $canApproveDocument, ApprovesDocument $approvesDocument, RejectsDocument $rejectsDocument, FetchesDocument $fetchesDocument, UpdatesCompanyStatus $updatesCompanyStatus, CreateNotificationProcessor $createNotificationProcessor) { $this->canApproveDocument = $canApproveDocument; $this->approvesDocument = $approvesDocument; $this->rejectsDocument = $rejectsDocument; $this->fetchesDocument = $fetchesDocument; $this->updatesCompanyStatus = $updatesCompanyStatus; + $this->createNotificationProcessor = $createNotificationProcessor; } /** @@ -84,6 +92,16 @@ class ApproveIdentificationDocumentLogic extends AbstractControllerLogic $this->updatesCompanyStatus->execute($document->owner, $status === 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED); + $object = new NotificationObject( + 'ID Verification ' . ( $status === 'approve' ? 'Approved' : 'Rejected' ), + ( $status === 'approve' ? 'Dear user, congratulations that your ' : 'Dear user, we are sorry to inform you that your ' ) . ( $document->type === 'IDENTITY_CARD' ? 'IC' : 'SSM' ) . ( $status === 'approve' ? ' has been approved. Start your first order now!' : ' has been rejected due to ' . ( $request->input('rejectRemark') ?? '' ) . ', please resubmit it for further action.' ), + $document->owner, + $document->owner->employees()->first(), + $document, + ); + + $this->createNotificationProcessor->execute($object); + return $this->resourceResponse(new DocumentResource($document)); } diff --git a/app/Classes/Modules/Companies/ControllersLogic/AssignCompanyToSegmentLogic.php b/app/Classes/Modules/Companies/ControllersLogic/AssignCompanyToSegmentLogic.php index 17a6c900..1148a6d6 100644 --- a/app/Classes/Modules/Companies/ControllersLogic/AssignCompanyToSegmentLogic.php +++ b/app/Classes/Modules/Companies/ControllersLogic/AssignCompanyToSegmentLogic.php @@ -2,11 +2,17 @@ namespace App\Classes\Modules\Companies\ControllersLogic; - +use App\Classes\Exceptions\MalformedRequestException; 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; use Illuminate\Http\Request; @@ -29,15 +35,25 @@ class AssignCompanyToSegmentLogic extends AbstractControllerLogic /** @var AssignSegmentProcessor */ private $assignCompanyToSegmentProcessor; + /** @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) + public function __construct(FetchesCompany $fetchesCompany, AssignSegmentProcessor $assignCompanyToSegmentProcessor, CreatesSeasonalSegment $createsSeasonalSegment, CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor) { $this->fetchesCompany = $fetchesCompany; $this->assignCompanyToSegmentProcessor = $assignCompanyToSegmentProcessor; + $this->createsSeasonalSegment = $createsSeasonalSegment; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; } /** @@ -54,6 +70,28 @@ class AssignCompanyToSegmentLogic extends AbstractControllerLogic $this->assignCompanyToSegmentProcessor->execute($company, $request->input('segment_id')); + if ($request->input('ending_on')) { + + if ($request->input('ending_on') && Carbon::parse($request->input('ending_on')) <= Carbon::now()) { + throw new MalformedRequestException('The Ending Date must be equal to or later than today\'s date.'); + } + + // todo-new + // if (count($company->seasonalSegments)) { + // throw new MalformedRequestException('System error. Please contact admin.'); + // } + + $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 bbf91ea7..6a818ea8 100644 --- a/app/Classes/Modules/Companies/ControllersLogic/CreateIdentificationDocumentLogic.php +++ b/app/Classes/Modules/Companies/ControllersLogic/CreateIdentificationDocumentLogic.php @@ -9,9 +9,12 @@ use App\Classes\Modules\Companies\Services\UpdatesCompanyStatus; use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; use App\Classes\Modules\Documents\Services\CreatesDocument; use App\Classes\Modules\Documents\Services\CreatesFiles; +use App\Classes\Modules\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; @@ -42,6 +45,11 @@ class CreateIdentificationDocumentLogic extends AbstractControllerLogic /** @var UpdatesCompanyStatus */ private $updatesCompanyStatus; + /** @var NewLeadTaskToPerfexCRMProcessor */ + private $newLeadTaskToPerfexCRMProcessor; + + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; /** * CreateIdentificationDocumentLogic constructor. @@ -49,13 +57,17 @@ class CreateIdentificationDocumentLogic extends AbstractControllerLogic * @param CreatesDocument $createsDocument * @param CreatesFiles $createsFile * @param UpdatesCompanyStatus $updatesCompanyStatus + * @param NewLeadTaskToPerfexCRMProcessor $newLeadTaskToPerfexCRMProcessor + * @param CheckMilestoneForRewardProcessor $checkMilestonesForRewardProcessor */ - public function __construct(FetchesCompany $fetchesCompany, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesCompanyStatus $updatesCompanyStatus) + 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; } /** @@ -78,7 +90,15 @@ class CreateIdentificationDocumentLogic extends AbstractControllerLogic $this->updatesCompanyStatus->execute($company, ApprovalStatus::PENDING_VERIFICATION); + if(config('perfexcrm.is_enabled') == 'true'){ + $this->newLeadTaskToPerfexCRMProcessor->execute($company); + } + + //cief todo: case study 2 + // $user = $company->employees()->first(); + // $this->checkMilestonesForRewardProcessor->execute($user, [Milestones::MILESTONE_2]); + return $this->response([]); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyLogic.php b/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyLogic.php index 5406d682..3c3fb088 100644 --- a/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyLogic.php +++ b/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyLogic.php @@ -52,10 +52,10 @@ class FetchCompanyLogic extends AbstractControllerLogic { $this->canFetchCompany->passes(); - $query = $this->fetchesCompany->execute(['id' => $request->route('id'), 'with_bookings' => true]); + $query = $this->fetchesCompany->execute(['id' => $request->route('id'), 'with_bookings' => true, 'with_wallets' => true]); return $this->resourceResponse(new CompanyResource($query)); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/ControllersLogic/ListBusinessTypesLogic.php b/app/Classes/Modules/Companies/ControllersLogic/ListBusinessTypesLogic.php new file mode 100644 index 00000000..82e840ec --- /dev/null +++ b/app/Classes/Modules/Companies/ControllersLogic/ListBusinessTypesLogic.php @@ -0,0 +1,44 @@ + 'Retrieved Business Types', + 'message' => 'You have successfully retrieved a list of Business Types' + ]; + } + + + /** + * @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 + { + $businessTypes = []; + + foreach(BusinessType::BUSINESS_TYPE_LIST as $key => $val) { + $type = (object)["id" => $key, "type" => $val]; + array_push($businessTypes, $type); + } + + return $this->collectionResponse(GeneralTypeResource::collection($businessTypes)); + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Companies/ControllersLogic/ListCompanyTypesLogic.php b/app/Classes/Modules/Companies/ControllersLogic/ListCompanyTypesLogic.php new file mode 100644 index 00000000..0a9afdfb --- /dev/null +++ b/app/Classes/Modules/Companies/ControllersLogic/ListCompanyTypesLogic.php @@ -0,0 +1,44 @@ + 'Retrieved Company Types', + 'message' => 'You have successfully retrieved a list of Company Types' + ]; + } + + + /** + * @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 + { + $businessTypes = []; + + foreach(CompanyType::COMPANY_TYPE_LIST as $key => $val) { + $type = (object)["id" => $key, "type" => $val]; + array_push($businessTypes, $type); + } + + return $this->collectionResponse(GeneralTypeResource::collection($businessTypes)); + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Companies/ControllersLogic/RemoveCompanyFromSegmentLogic.php b/app/Classes/Modules/Companies/ControllersLogic/RemoveCompanyFromSegmentLogic.php index 10623311..22c89a45 100644 --- a/app/Classes/Modules/Companies/ControllersLogic/RemoveCompanyFromSegmentLogic.php +++ b/app/Classes/Modules/Companies/ControllersLogic/RemoveCompanyFromSegmentLogic.php @@ -53,11 +53,12 @@ class RemoveCompanyFromSegmentLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { - $company = $this->fetchesCompany->execute(['id' => $request->route('id')]); $segment = $this->fetchesSegment->execute(['id' => $request->route('segment_id')]); + $company->seasonalSegments()->where('segment_id', $request->route('segment_id'))->delete(); + $this->removesCompanyFromSegment->execute($company, $segment); return $this->resourceResponse(new CompanyResource($company)); diff --git a/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyNameAndDebtorLogic.php b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyNameAndDebtorLogic.php new file mode 100644 index 00000000..05fb0746 --- /dev/null +++ b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyNameAndDebtorLogic.php @@ -0,0 +1,82 @@ + 'Update Company Account Status', + 'message' => 'You have successfully updated the Company Account Status' + ]; + } + /** @var CanUpdateCompany */ + private $canUpdateCompany; + + /** @var UpdatesCompany */ + private $updatesCompany; + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** @var UpdatesCompanyDebtor */ + private $updatesCompanyDebtor; + + /** + * UpdateCompanyControllersLogic constructor. + * @param CanUpdateCompany $canUpdateCompany + * @param UpdatesCompany $updatesCompany + * @param FetchesCompany $fetchesCompany + * @param UpdatesCompanyDebtor $updatesCompanyDebtor + */ + public function __construct( + CanUpdateCompany $canUpdateCompany, + UpdatesCompany $updatesCompany, + FetchesCompany $fetchesCompany, + UpdatesCompanyDebtor $updatesCompanyDebtor + ) + { + $this->canUpdateCompany = $canUpdateCompany; + $this->updatesCompany = $updatesCompany; + $this->fetchesCompany = $fetchesCompany; + $this->updatesCompanyDebtor = $updatesCompanyDebtor; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws ErrorException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->fetchesCompany->execute(['id' => $request->route('id')]); + + $object = new CompanyObject($request->input('name'), $request->input('reference'), $query->business_type, $request->input('type')); + + $this->canUpdateCompany->passes($object); + + $query = $this->updatesCompany->execute($query, $object); + + if ($request->input('debtor') || $query->first()->debtor !== null) { + $this->updatesCompanyDebtor->execute($query, $request->input('debtor')); + } + + return $this->resourceResponse(new CompanyResource($query)); + } + +} + diff --git a/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyProfileLogic.php b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyProfileLogic.php new file mode 100644 index 00000000..e25fdbf6 --- /dev/null +++ b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyProfileLogic.php @@ -0,0 +1,65 @@ + 'Update Company Details', + 'message' => 'You have successfully updated the Company Details' + ]; + } + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** @var UpdatesCompanyProfile */ + private $updatesCompanyProfile; + + /** + * UpdateCompanyProfileLogic constructor. + * @param FetchesCompany $fetchesCompany + * @param UpdatesCompanyProfile $updatesCompanyProfile + */ + public function __construct( + FetchesCompany $fetchesCompany, + UpdatesCompanyProfile $updatesCompanyProfile + ) + { + $this->fetchesCompany = $fetchesCompany; + $this->updatesCompanyProfile = $updatesCompanyProfile; + } + + /** + * @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 + { + $object = new CompanyProfileObject($request->input('email'), $request->input('phone'), $request->input('type')); + + $company = $this->fetchesCompany->execute(['id' => $request->route('id')]); + + $company_query = $this->updatesCompanyProfile->execute($company, $object); + + return $this->resourceResponse(new CompanyResource($company_query)); + + } + +} \ No newline at end of file diff --git a/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyStatusLogic.php b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyStatusLogic.php new file mode 100644 index 00000000..92278bef --- /dev/null +++ b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyStatusLogic.php @@ -0,0 +1,62 @@ + 'Update Company Account Status', + 'message' => 'You have successfully updated the Company Account Status' + ]; + } + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** @var UpdatesCompanyStatus */ + private $updatesCompanyStatus; + + /** + * UpdateCompanyStatusLogic constructor. + * @param FetchesCompany $fetchesCompany + * @param UpdatesCompanyStatus $updatesCompanyStatus + */ + public function __construct( + FetchesCompany $fetchesCompany, + UpdatesCompanyStatus $updatesCompanyStatus + ) + { + $this->fetchesCompany = $fetchesCompany; + $this->updatesCompanyStatus = $updatesCompanyStatus; + } + + /** + * @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 + { + $company = $this->fetchesCompany->execute(['id' => $request->route('id')]); + + $company_query = $this->updatesCompanyStatus->execute($company, $request->input('status')); + + return $this->resourceResponse(new CompanyResource($company_query)); + + } + +} \ No newline at end of file diff --git a/app/Classes/Modules/Companies/DataTransferObjects/CompanyObject.php b/app/Classes/Modules/Companies/DataTransferObjects/CompanyObject.php index 09b0e153..cbc5df53 100644 --- a/app/Classes/Modules/Companies/DataTransferObjects/CompanyObject.php +++ b/app/Classes/Modules/Companies/DataTransferObjects/CompanyObject.php @@ -83,4 +83,4 @@ class CompanyObject implements DataTransferObject -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/DataTransferObjects/CompanyProfileObject.php b/app/Classes/Modules/Companies/DataTransferObjects/CompanyProfileObject.php new file mode 100644 index 00000000..0f3e8a54 --- /dev/null +++ b/app/Classes/Modules/Companies/DataTransferObjects/CompanyProfileObject.php @@ -0,0 +1,55 @@ +email = $email; + $this->phone = $phone; + $this->companyType = $companyType; + } + + /** + * @return null|string + */ + public function getEmail(): ?string + { + return $this->email; + } + + /** + * @return string + */ + public function getPhone(): string + { + return $this->phone; + } + + /** + * @return int + */ + public function getCompanyType(): int + { + return $this->companyType; + } +} diff --git a/app/Classes/Modules/Companies/DataTransferObjects/CompanySegmentObject.php b/app/Classes/Modules/Companies/DataTransferObjects/CompanySegmentObject.php new file mode 100644 index 00000000..b00ef56c --- /dev/null +++ b/app/Classes/Modules/Companies/DataTransferObjects/CompanySegmentObject.php @@ -0,0 +1,46 @@ +company = $company; + $this->segment = $segment; + } + + /** + * @return Company + */ + public function getCompany(): Company + { + return $this->company; + } + + /** + * @return Segment + */ + public function getSegment(): Segment + { + return $this->segment; + } + +} diff --git a/app/Classes/Modules/Companies/Processors/AssignSegmentProcessor.php b/app/Classes/Modules/Companies/Processors/AssignSegmentProcessor.php index 66cfb08e..380f7a87 100644 --- a/app/Classes/Modules/Companies/Processors/AssignSegmentProcessor.php +++ b/app/Classes/Modules/Companies/Processors/AssignSegmentProcessor.php @@ -2,12 +2,14 @@ namespace App\Classes\Modules\Companies\Processors; +use App\Classes\Modules\Companies\DataTransferObjects\CompanySegmentObject; use App\Classes\Modules\Companies\Services\AssignsCompanyToSegment; use App\Classes\Modules\Companies\Standards\Rules\CanAssignSegment; use App\Classes\Modules\Segments\Services\FetchesSegment; use App\Classes\ValueObjects\Constants\SegmentConstants; use App\Models\Company; use Illuminate\Database\Eloquent\Model; +use Illuminate\Validation\ValidationException; class AssignSegmentProcessor { @@ -47,10 +49,18 @@ class AssignSegmentProcessor $segment = $this->fetchesSegment->execute(['id' => $segmentId]); - $this->canAssignSegment->passes(); + $companySegment = new CompanySegmentObject($company, $segment); + + try + { + $this->canAssignSegment->passes($companySegment); + } + catch (\Exception $e) { + return $company; + } return $this->assignsSegment->execute($company, $segment); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/Processors/CreateCompanyProcessor.php b/app/Classes/Modules/Companies/Processors/CreateCompanyProcessor.php index fc84e9cc..ad9abeb8 100644 --- a/app/Classes/Modules/Companies/Processors/CreateCompanyProcessor.php +++ b/app/Classes/Modules/Companies/Processors/CreateCompanyProcessor.php @@ -46,9 +46,10 @@ class CreateCompanyProcessor public function execute(Request $request, int $businessType = BusinessType::IMPORTER, ?int $companyType = CompanyType::COMPANY_BUSINESS, ?int $status = ApprovalStatus::PENDING_SUBMISSION): Model { $companyName = $companyType === CompanyType::COMPANY_BUSINESS ? $request->input('company_name') : $request->input('name'); + $companyReference = $request->input('company_reference') ? $request->input('company_reference') : mt_rand(1000, 9999).(new GeneratesInitials())->name($companyName)->length(3)->generate(); $company_object = new CompanyObject( $companyName, - mt_rand(1000, 9999).(new GeneratesInitials())->name($companyName)->length(3)->generate(), + $companyReference, $businessType, $companyType, $status); $this->canCreateCompany->passes($company_object); diff --git a/app/Classes/Modules/Companies/Services/FetchesCompanyServiceSettings.php b/app/Classes/Modules/Companies/Services/FetchesCompanyServiceSettings.php index 55a63803..5f84b81d 100644 --- a/app/Classes/Modules/Companies/Services/FetchesCompanyServiceSettings.php +++ b/app/Classes/Modules/Companies/Services/FetchesCompanyServiceSettings.php @@ -29,7 +29,7 @@ class FetchesCompanyServiceSettings $constants= $service->constants()->whereIn('segment_id', $company->segments->pluck('id'))->get(); - $standardConfigurations = $constants->firstWhere('reference', SegmentConstants::SERVICE_TYPE); + $standardConfigurations = $constants->where('reference', '=', SegmentConstants::SERVICE_TYPE)->first(); $rate = Currency::find($object->getCurrencyId())->rates->where('payment_method_type', $object->getPaymentMethod()) ->where('service_id', $object->getServiceId())->first(); @@ -105,4 +105,4 @@ class FetchesCompanyServiceSettings -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/Services/UpdatesCompany.php b/app/Classes/Modules/Companies/Services/UpdatesCompany.php index f89bbca8..1b92c2d8 100644 --- a/app/Classes/Modules/Companies/Services/UpdatesCompany.php +++ b/app/Classes/Modules/Companies/Services/UpdatesCompany.php @@ -15,7 +15,7 @@ class UpdatesCompany extends AbstractUpdateRecord * @return \Illuminate\Database\Eloquent\Model * @throws \App\Classes\Exceptions\MalformedRequestException */ - public function execute(Company $model, CompanyObject $object) + public function execute(Company $model, CompanyObject $object) { $model->name = $object->getName(); $model->reference = $object->getReference(); @@ -23,4 +23,4 @@ class UpdatesCompany extends AbstractUpdateRecord return $this->handler($model); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/Services/UpdatesCompanyDebtor.php b/app/Classes/Modules/Companies/Services/UpdatesCompanyDebtor.php index 4cb43a9f..14e0d6e2 100644 --- a/app/Classes/Modules/Companies/Services/UpdatesCompanyDebtor.php +++ b/app/Classes/Modules/Companies/Services/UpdatesCompanyDebtor.php @@ -11,6 +11,7 @@ class UpdatesCompanyDebtor extends AbstractUpdateRecord /** * @param Company $model + * @param CompanyObject $object * @param string $debtor * @return \Illuminate\Database\Eloquent\Model * @throws \App\Classes\Exceptions\MalformedRequestException @@ -18,6 +19,7 @@ class UpdatesCompanyDebtor extends AbstractUpdateRecord public function execute(Company $model, string $debtor) { $model->debtor = $debtor; + return $this->handler($model); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/Services/UpdatesCompanyProfile.php b/app/Classes/Modules/Companies/Services/UpdatesCompanyProfile.php new file mode 100644 index 00000000..85da8836 --- /dev/null +++ b/app/Classes/Modules/Companies/Services/UpdatesCompanyProfile.php @@ -0,0 +1,32 @@ +type = $object->getCompanyType(); + + $contact = $model->contacts()->first(); + if ($contact) { + $contact->email = $object->getEmail(); + $contact->phone = $object->getPhone(); + $contact->save(); + } + + return $this->handler($model); + + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Companies/Standards/Rules/CanAssignSegment.php b/app/Classes/Modules/Companies/Standards/Rules/CanAssignSegment.php index 36fd2a41..c6dab1d9 100644 --- a/app/Classes/Modules/Companies/Standards/Rules/CanAssignSegment.php +++ b/app/Classes/Modules/Companies/Standards/Rules/CanAssignSegment.php @@ -4,11 +4,22 @@ namespace App\Classes\Modules\Companies\Standards\Rules; use App\Classes\General\Abstracts\AbstractRule; -use App\Classes\Modules\SegmentConstants\DataTransferObjects\SegmentConstantObject; -use App\Classes\Modules\Segments\DataTransferObjects\ConstantObject; +use App\Classes\Modules\Companies\Standards\Validators\CompanyToSegmentValidation; +use App\Classes\Modules\Companies\DataTransferObjects\CompanySegmentObject; class CanAssignSegment extends AbstractRule { + /** @var CompanyToSegmentValidation */ + private $companyToSegmentValidation; + + /** + * CanAssignSegment constructor. + * @param CompanyToSegmentValidation $companyToSegmentValidation + */ + public function __construct(CompanyToSegmentValidation $companyToSegmentValidation) + { + $this->companyToSegmentValidation = $companyToSegmentValidation; + } /** * @return bool @@ -20,16 +31,17 @@ class CanAssignSegment extends AbstractRule } /** - * @param ConstantObject $object + * @param CompanySegmentObject $object * @return bool + * @throws \App\Classes\Exceptions\RequestValidationException */ protected function validators($object): bool { - return true; + return $this->companyToSegmentValidation->validate($object); } /** - * @param ConstantObject $object + * @param CompanySegmentObject $object * @return bool */ protected function criteria($object): bool @@ -37,4 +49,4 @@ class CanAssignSegment extends AbstractRule return true; } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/Standards/Validators/CompanyToSegmentValidation.php b/app/Classes/Modules/Companies/Standards/Validators/CompanyToSegmentValidation.php new file mode 100644 index 00000000..5151a275 --- /dev/null +++ b/app/Classes/Modules/Companies/Standards/Validators/CompanyToSegmentValidation.php @@ -0,0 +1,51 @@ + $object + ]; + } + + /** + * @return array + */ + protected function rules(): array + { + return [ + 'company_segment' => [ + 'required', + function ($attribute, $obj, $fail) { + // $result = SegmentCompany::where('segment_id', $obj->getSegment()->id)->where('company_id', $obj->getCompany()->id)->first(); + $result = $obj->getCompany()->segments()->where('segment_id', $obj->getSegment()->id)->first(); + Log::info('SegmentCompany: '.json_encode($result)); + if (!is_null($result)) { + $fail('The segment company already exists'); + } + }, + ] + ]; + } + + /** + * @return array + */ + protected function messages(): array + { + return []; + } + +} diff --git a/app/Classes/Modules/Currencies/ControllersLogic/History/ListCurrencyRateHistoryLogic.php b/app/Classes/Modules/Currencies/ControllersLogic/History/ListCurrencyRateHistoryLogic.php new file mode 100644 index 00000000..aae7d3aa --- /dev/null +++ b/app/Classes/Modules/Currencies/ControllersLogic/History/ListCurrencyRateHistoryLogic.php @@ -0,0 +1,84 @@ + 'Retrieved Currency Rate History', + 'message' => 'You have successfully retrieved a list of Currency Rate History' + ]; + } + + /** @var CanListRates */ + private $canListRates; + + /** @var ListsRateLogs */ + private $listsRateLogs; + + /** @var ListsRate */ + private $listsRates; + + /** + * ListRatesLogic constructor. + * @param CanListRates $canListRates + * @param ListsRates $listsRates + */ + public function __construct(CanListRates $canListRates, ListsRateLogs $listsRateLogs, listsRates $listsRates) + { + $this->canListRates = $canListRates; + $this->listsRateLogs = $listsRateLogs; + $this->listsRates = $listsRates; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws ErrorException + */ + public function logic(Request $request): JsonResponse + { + //$this->canListRates->passes(); + + $filterArray = json_decode($request->input('filters')); + + $rate_filter['currency_id'] = $filterArray->currency_id; + $rate_filter['service_id'] = $filterArray->service_id; + + $currency_rate_ids = $this->listsRates->execute( + [ + 'currency_id' => $filterArray->currency_id, + 'service_id' => $filterArray->service_id, + ] + )->pluck('id')->toArray(); + + $query = $this->listsRateLogs->execute( + [ + 'currency_rate_id_in' => $currency_rate_ids, + 'date_start' => new Carbon($filterArray->date_from), + 'date_end' => new Carbon($filterArray->date_to), + ] + ); + + return $this->collectionResponse(CurrencyRateLogResource::collection($query)); + } +} diff --git a/app/Classes/Modules/Currencies/Services/Rates/ListsRateLogs.php b/app/Classes/Modules/Currencies/Services/Rates/ListsRateLogs.php new file mode 100644 index 00000000..d3cfa469 --- /dev/null +++ b/app/Classes/Modules/Currencies/Services/Rates/ListsRateLogs.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} \ No newline at end of file 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/ExportsAnalyticBillingTransactions.php b/app/Classes/Modules/Exports/Services/ExportsAnalyticBillingTransactions.php new file mode 100644 index 00000000..af4c88c4 --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsAnalyticBillingTransactions.php @@ -0,0 +1,91 @@ +request = $request; + } + + public function headings(): array + { + return [ + 'Booking Id', + 'Service Type', + 'Payment Method', + 'Company Id', + 'Customer Segment', + 'Company Create Date', + 'Company Type', + 'Supplier Id', + 'Customer Payment', + 'Customer Rate', + 'Customer Service Charge', + 'Customer Payment Date', + 'Original Amount', + 'Supplier Payment', + 'Supplier Rate', + 'Supplier Service Charge', + 'Supplier Payment Date' + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function collection() + { + return Transaction::where('type', 3)->get(); + } + + /** + * @param $transaction + * @return array + */ + public function map($transaction): array + { + + $bill = $transaction; + $payment = $transaction->owner; + $booking = $payment->owner; + $company = $booking->company; + $ecommerce = str::contains($booking->bank->bank_name, ['浙江网商银行']); + + return [ + $booking->id, + $ecommerce ? '1688' : ($booking->service->id === 4 ? '1688' : $booking->service->name ), + PaymentMethodType::PAYMENT_METHODS_ID[$payment->payment_method], + $company->id, + $company->segments->implode('name', ', '), + $company->created_at, + $company->type === 0 ? 'Personal' : 'Company', + $bill->issuerCompany->name, + $payment->amount, + $payment->currency_rate, + $payment->service_charge, + $payment->created_at, + $payment->original_amount, + $bill->amount, + $bill->currency_rate, + $bill->service_charge, + $bill->created_at + ]; + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Exports/Services/ExportsAnalyticBookingTransactions.php b/app/Classes/Modules/Exports/Services/ExportsAnalyticBookingTransactions.php new file mode 100644 index 00000000..c1e2af71 --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsAnalyticBookingTransactions.php @@ -0,0 +1,107 @@ +request = $request; + } + + public function headings(): array + { + return [ + 'TransID', + 'Company ID', + 'Marking', + 'Type Of User', + 'Group Control / Experiment', + 'Payment Date', + 'Completed Purchase OrderDate', + 'DayTo Closed', + 'IsCompleted Purchase Order (1 or 0)', + 'Final Payment', + 'Discount Amount', + 'PaymentType', + 'RowUpdateDate' + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + return Booking::query(); + } + + /** + * @param $booking + * @return array + */ + public function map($booking): array + { + $companyType = []; + $companyType[companyType::COMPANY_BUSINESS] = 'COMPANY_BUSINESS'; + $companyType[companyType::PERSONAL_BUSINESS] = 'PERSONAL_BUSINESS'; + + $payments = $booking->transactions()->where('type', 1)->whereIn('status', [2, 3]); + + $paymentmethondArray = PaymentMethodType::PAYMENT_METHODS_ID; + $paymentmethondArray[0] = 'Hybrid'; + $paymentMethod = ''; + foreach ($payments->get() as $payment) { + if ( $paymentMethod == '' ) { + $paymentMethod = $payment->payment_method; + } else if ( $payment->payment_method != $paymentMethod ) { + $paymentMethod = 0; // set it to Hybrid + } + } + $paymentMethod === '' ? '' : $paymentMethod = $paymentmethondArray[$paymentMethod]; + + $firstPayment = $payments->first(); + + $CompletedPurchaseOrders = $booking->transactions()->where('type', 7)->whereIn('status', [1, 2]); + + $FirstCompletedPurchaseOrder = $CompletedPurchaseOrders->first(); + + $lastPayment = $booking->transactions()->where('type', 1)->whereIn('status', [2, 3])->orderBy('id', 'desc')->first(); + + return [ + $booking->id, + $booking->company_id, + $booking->company->reference, + '', +// $companyType[$booking->company->type], + '', + $firstPayment == null ? '' : $firstPayment->created_at, + $FirstCompletedPurchaseOrder == null ? '' : $FirstCompletedPurchaseOrder->created_at, + '', + $CompletedPurchaseOrders->count() > 0 ? '1' : '0', + $lastPayment == null ? '' : $lastPayment->amount, + '', + $paymentMethod, + '', + ]; + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Exports/Services/ExportsBookingTransactions.php b/app/Classes/Modules/Exports/Services/ExportsBookingTransactions.php new file mode 100644 index 00000000..aed106ea --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsBookingTransactions.php @@ -0,0 +1,74 @@ +request = $request; + } + + public function headings(): array + { + return [ + 'Ref No', + 'Creted Date', + 'Amount', + 'Rate', + 'Supplier' + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + $supplierIds = array_map(function($value){ + return ['id' => $value]; + }, json_decode($this->request->input('supplierIds'))); + + $dateFrom =Carbon::parse($this->request->input('startDate'))->format('Y-m-d'); + $dateTo =Carbon::parse($this->request->input('endDate'))->format('Y-m-d'); + + return Transaction::where('type', 3)->whereIn('issuer', $supplierIds)->whereBetween('created_at', [$dateFrom, $dateTo]); + } + + /** + * @param $transaction + * @return array + */ + public function map($transaction): array + { + $supplierName = Company::where('id', $transaction->issuer)->get()->first()->name; + $refNo = $transaction->owner->owner == null ? $transaction->owner->marking : $transaction->owner->owner->marking; + + $createdAt = $transaction->created_at->format('d-m-Y'); + $amount = $transaction->amount; + $rate = $transaction->currency_rate; + + return [ + $refNo, + $createdAt, + $amount, + $rate, + $supplierName + ]; + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Exports/Services/ExportsCustomers.php b/app/Classes/Modules/Exports/Services/ExportsCustomers.php index 1972935e..9f314030 100644 --- a/app/Classes/Modules/Exports/Services/ExportsCustomers.php +++ b/app/Classes/Modules/Exports/Services/ExportsCustomers.php @@ -7,21 +7,26 @@ use App\Classes\ValueObjects\Constants\CompanyType; use App\Models\Company; use Maatwebsite\Excel\Concerns\Exportable; use Maatwebsite\Excel\Concerns\FromQuery; +use Maatwebsite\Excel\Concerns\ShouldAutoSize; use Maatwebsite\Excel\Concerns\WithHeadingRow; +use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithMapping; -class ExportsCustomers implements FromQuery, WithHeadingRow, WithMapping +class ExportsCustomers implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize { - use Exportable; public function headings(): array { return [ 'Marking', - 'Name', + 'Company Name', + 'Email', + 'Account Name', + 'Phone Number', 'Business type', 'Number of bookings', + 'Last booking Date', 'Bookings total', 'Registration Date', 'System Registration' @@ -44,13 +49,18 @@ class ExportsCustomers implements FromQuery, WithHeadingRow, WithMapping public function map($company): array { + $employee = $company->employees()->first(); + $lastBooking = $company->bookings()->orderByDesc('id')->first(); + return [ $company->reference, $company->name, - $company->employees()->first() ? $company->employees()->first()->name : '', + $employee ? $employee->email : '', + $employee ? $employee->name : '', $company->contacts()->first() ? $company->contacts()->first()->phone : '', $company->type === CompanyType::COMPANY_BUSINESS ? 'Company' : 'individual', count($company->bookings), + $lastBooking ? $lastBooking->created_at : '', $company->bookings()->sum('fix_amount'), \PhpOffice\PhpSpreadsheet\Shared\Date::dateTimeToExcel($company->created_at), count($company->segments()->where('segment_id', '=', 5)->get()) ? 'Exchange 1.0' : 'Exchange 2.0' 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/ExportsLeadsTransactions.php b/app/Classes/Modules/Exports/Services/ExportsLeadsTransactions.php new file mode 100644 index 00000000..4b8581d6 --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsLeadsTransactions.php @@ -0,0 +1,71 @@ +request = $request; + $this->listsCompanies = $listsCompanies; + } + + public function headings(): array + { + return [ + 'Agent Name', + 'User Name', + 'Contact Number', + 'Email', + 'Customer Code', + 'Register Date' + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + $query = $this->listsCompanies->execute( + [ + 'business_type' => 2, + 'with_bookings' => true, + 'without_confirmed_payments' => true, + 'does_not_have_segments' => [5], + ] + ); + + return $query->toQuery(); + } + + /** + * @param $row + * @return array + */ + public function map($row): array + { + return [ + '', + $row->name, + strval($row->contacts->first()->phone), + // $row->contacts->first()->email, + $row->employees()->first() == null ? '' : $row->employees()->first()->email, + $row->reference, + $row->created_at->format('d-m-Y') + ]; + } +} diff --git a/app/Classes/Modules/Exports/Services/ExportsNullDebtors.php b/app/Classes/Modules/Exports/Services/ExportsNullDebtors.php index be216473..53a71772 100644 --- a/app/Classes/Modules/Exports/Services/ExportsNullDebtors.php +++ b/app/Classes/Modules/Exports/Services/ExportsNullDebtors.php @@ -26,7 +26,6 @@ class ExportsNullDebtors implements FromQuery, WithHeadings, WithHeadingRow, Wit 'Code', 'DebtorControlAcc', 'ControlAccount', - 'AccNo', 'CompanyName', 'Desc2', 'DebtorType', @@ -56,8 +55,12 @@ class ExportsNullDebtors implements FromQuery, WithHeadings, WithHeadingRow, Wit { return Company::where(function($query){ $query->whereNull('debtor')->orWhere('debtor', ''); - })->whereNotIn('id', [2207, 2248, 2029])->where('business_type', BusinessType::IMPORTER)->where('status', ApprovalStatus::APPROVED)->whereHas('transactions', function($query){ - return $query->whereIn('type', [TransactionType::PAYMENT, TransactionType::TOP_UP])->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED, ApprovalStatus::PENDING_VERIFICATION]); + })->whereNotIn('id', [2207, 2248, 2029])->where('business_type', BusinessType::IMPORTER)->where('status', ApprovalStatus::APPROVED)->where(function($query){ + $query->whereHas('transactions', function($query){ + return $query->whereIn('type', [TransactionType::PAYMENT, TransactionType::TOP_UP])->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED, ApprovalStatus::PENDING_VERIFICATION]); + })->orWhereHas('wallets', function($query){ + return $query->whereHas('transactions'); + }); }); } @@ -72,7 +75,6 @@ class ExportsNullDebtors implements FromQuery, WithHeadings, WithHeadingRow, Wit '<>', '300-0000', '300-0000', - '300-0000', $company->name.' (PURCHASE)', $company->reference, '', diff --git a/app/Classes/Modules/Exports/Services/ExportsPaymentTransactions.php b/app/Classes/Modules/Exports/Services/ExportsPaymentTransactions.php index 0ca1d730..31d814a0 100644 --- a/app/Classes/Modules/Exports/Services/ExportsPaymentTransactions.php +++ b/app/Classes/Modules/Exports/Services/ExportsPaymentTransactions.php @@ -32,14 +32,17 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading 'DocNo', 'DocDate', 'DebtorCode', + 'Ref', 'DebtorName', 'CurrencyCode', 'ShipInfo', 'ItemCode', 'DetailDescription', + 'FurtherDescription', 'Qty', 'UnitPrice', - 'AccNo' + 'AccNo', + 'DeptNo' ]; } @@ -48,14 +51,14 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading */ public function query() { - $start_date = $this->request->input('start_date', null); + $start_date = $this->request->input('startDate', null); if ($start_date) { - $start_date = Carbon::parse($this->request->input('start_date'))->format('Y-m-d'); + $start_date = Carbon::parse($this->request->input('startDate'))->format('Y-m-d'); } - $end_date = $this->request->input('end_date', null); + $end_date = $this->request->input('endDate', null); if ($end_date) { - $end_date = Carbon::parse($this->request->input('end_date'))->format('Y-m-d'); + $end_date = Carbon::parse($this->request->input('endDate'))->format('Y-m-d'); } $query = Transaction::query(); @@ -65,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') ]); } @@ -91,16 +94,19 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading return [ '<>', - $transaction->updated_at->format('d/m/Y'), + $transaction->updated_at->format('m/d/Y H:m'), $company->debtor, + $booking->marking, '', 'MYR', $booking->marking, '', 'PLEASE REFER TO THE ATTACHED APPENDIX REF ' . $booking->marking, + '', 1, - $transaction->amount, - '500-0000' + round($transaction->amount, 2), + '500-0000', + 'CIEF' ]; } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Exports/Services/ExportsTransactions.php b/app/Classes/Modules/Exports/Services/ExportsTransactions.php index f9bbecaf..6d6751bf 100644 --- a/app/Classes/Modules/Exports/Services/ExportsTransactions.php +++ b/app/Classes/Modules/Exports/Services/ExportsTransactions.php @@ -5,14 +5,17 @@ namespace App\Classes\Modules\Exports\Services; use App\Classes\ValueObjects\Constants\BusinessType; use App\Classes\ValueObjects\Constants\CompanyType; use App\Classes\ValueObjects\Constants\TransactionType; +use App\Models\Booking; use App\Models\Company; use App\Models\Transaction; +use App\Models\Wallet; use Maatwebsite\Excel\Concerns\Exportable; use Maatwebsite\Excel\Concerns\FromQuery; use Maatwebsite\Excel\Concerns\WithHeadingRow; use Maatwebsite\Excel\Concerns\WithMapping; +use Maatwebsite\Excel\Concerns\ShouldAutoSize; -class ExportsTransactions implements FromQuery, WithHeadingRow, WithMapping +class ExportsTransactions implements FromQuery, WithHeadingRow, WithMapping, ShouldAutoSize { use Exportable; @@ -23,7 +26,7 @@ class ExportsTransactions implements FromQuery, WithHeadingRow, WithMapping */ public function query() { - return Transaction::query(); + return Transaction::whereIn('type', [TransactionType::PAYMENT, TransactionType::BILL])->where('owner_type', '!=',Wallet::class); } /** @@ -58,16 +61,28 @@ class ExportsTransactions implements FromQuery, WithHeadingRow, WithMapping 6 => 'EXPIRED' ]; + + $booking = $transaction->owner; + if(!($booking instanceof Booking)){ + $booking = $booking->owner; + } + + $company = $booking->company; + + if(!$company instanceof Company) dd($transaction); + return [ + $company->reference, + $booking ? $booking->marking : '', $transaction->bill_no, $transaction->owner_id, $transaction->owner_type, $transactionTypes[$transaction->type], - $transaction->issuer, + $transaction->issuerCompany->name, $transaction->reciver, - $transaction->currency_id === 1 ? 'MYR' : 'RMB', + $transaction->currency->short_code, $transaction->amount, - $transaction->original_currency_id === 1 ? 'MYR' : 'RMB', + $transaction->original_currency->short_code, $transaction->original_amount, $transaction->currency_rate, $transaction->tax, @@ -75,6 +90,7 @@ class ExportsTransactions implements FromQuery, WithHeadingRow, WithMapping $transactionStatus[$transaction->status], \PhpOffice\PhpSpreadsheet\Shared\Date::dateTimeToExcel($transaction->created_at), \PhpOffice\PhpSpreadsheet\Shared\Date::dateTimeToExcel($transaction->updated_at), +// $marking ]; } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Exports/Services/ExportsWalletTransactions.php b/app/Classes/Modules/Exports/Services/ExportsWalletTransactions.php index af4d2d07..2b915038 100644 --- a/app/Classes/Modules/Exports/Services/ExportsWalletTransactions.php +++ b/app/Classes/Modules/Exports/Services/ExportsWalletTransactions.php @@ -6,6 +6,7 @@ use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Models\Transaction; +use App\Models\Wallet; use Maatwebsite\Excel\Concerns\Exportable; use Maatwebsite\Excel\Concerns\FromQuery; use Maatwebsite\Excel\Concerns\ShouldAutoSize; @@ -37,9 +38,11 @@ class ExportsWalletTransactions implements FromQuery, WithHeadings, WithHeadingR 'ShipInfo', 'ItemCode', 'DetailDescription', + 'FurtherDescription', 'Qty', 'UnitPrice', - 'AccNo' + 'AccNo', + 'DeptNo' ]; } @@ -48,19 +51,27 @@ class ExportsWalletTransactions implements FromQuery, WithHeadings, WithHeadingR */ public function query() { - $start_date = $this->request->input('start_date', null); - if ($start_date) { - $start_date = Carbon::parse($this->request->input('start_date'))->format('Y-m-d'); + switch ($this->request->input('type', null)){ + case 'payment': $type = TransactionType::PAYMENT; break; + case 'credit_note': $type = TransactionType::CREDIT_NOTE; break; + case 'debit_note': $type = TransactionType::DEBIT_NOTE; break; + default : $type = TransactionType::TOP_UP; } - $end_date = $this->request->input('end_date', null); + $start_date = $this->request->input('startDate', null); + if ($start_date) { + $start_date = Carbon::parse($this->request->input('startDate'))->format('Y-m-d'); + } + + $end_date = $this->request->input('endDate', null); if ($end_date) { - $end_date = Carbon::parse($this->request->input('end_date'))->format('Y-m-d'); + $end_date = Carbon::parse($this->request->input('endDate'))->format('Y-m-d'); } $query = Transaction::query(); - $query->where('type', TransactionType::TOP_UP); + $query->where('type', $type); + $query->where('owner_type', Wallet::class); $query->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); if($start_date && $end_date) { @@ -80,7 +91,7 @@ class ExportsWalletTransactions implements FromQuery, WithHeadings, WithHeadingR } /** - * @param Company $transaction + * @param Transaction $transaction * * @return array */ @@ -90,16 +101,18 @@ class ExportsWalletTransactions implements FromQuery, WithHeadings, WithHeadingR return [ '<>', - $transaction->updated_at->format('d/m/Y'), + $transaction->updated_at->format('m/d/Y H:m'), $company->debtor, '', 'MYR', $transaction->bill_no, 'W1', 'CREDIT SALES', + '', 1, - $transaction->amount, - '500-0000' + round($transaction->amount, 2), + '500-0000', + 'WALLET' ]; } } \ 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/Imports/Services/GenericImport.php b/app/Classes/Modules/Imports/Services/GenericImport.php new file mode 100644 index 00000000..b2ffdb8f --- /dev/null +++ b/app/Classes/Modules/Imports/Services/GenericImport.php @@ -0,0 +1,23 @@ +rows = $collection; + } +} diff --git a/app/Classes/Modules/Imports/Services/ImportsBankRecord.php b/app/Classes/Modules/Imports/Services/ImportsBankRecord.php new file mode 100644 index 00000000..fa972e40 --- /dev/null +++ b/app/Classes/Modules/Imports/Services/ImportsBankRecord.php @@ -0,0 +1,62 @@ +where('type', TransactionType::PAYMENT)->where('owner_type', Booking::class) + ->WhereDate('created_at', $date->format('Y-m-d')) + ->where('amount', '>=', $credit)->where('amount', '<', ($credit + 0.01)) + ->get(); + + if(count($transaction)) { + $systemReference = $transaction->pluck('owner.marking')->flatten()->implode(', '); + } + + $matches = $systemReference === $collection['remarkreferences'] ? 'Yes' : 'No'; + + echo ' + '.$date->format('d-m-Y').' + '.$collection['description'].' + '.$credit.' + '.$systemReference.' + '.$collection['remarkreferences'].' + '.$matches.' + '; + + } + + public function batchSize(): int + { + return 100; + } + + public function rules(): array + { + return [ + + ]; + } +} 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/Notifications/ControllersLogic/ListNotificationsLogic.php b/app/Classes/Modules/Notifications/ControllersLogic/ListNotificationsLogic.php new file mode 100644 index 00000000..0fdbf6f9 --- /dev/null +++ b/app/Classes/Modules/Notifications/ControllersLogic/ListNotificationsLogic.php @@ -0,0 +1,61 @@ + 'Retrieve Notifications', + 'message' => 'You have successfully retrieved a list of Notifications' + ]; + } + + /** @var ListsNotification */ + private $listsNotification; + + /** + * ListNotificationsLogic constructor. + * @param ListsNotification $listsNotification + */ + public function __construct( + ListsNotification $listsNotification + ) + { + $this->listsNotification = $listsNotification; + } + + + /** + * @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 + { + + $filters = [ + // 'target_id'=>auth()->user()->id, + // 'per_page'=>$request->route('per_page') + ]; + + $notifications = $this->listsNotification->execute($filters); + + return $this->collectionResponse(NotificationResource::collection($notifications)); + + } + +} \ No newline at end of file diff --git a/app/Classes/Modules/Notifications/DataTransferObjects/NotificationObject.php b/app/Classes/Modules/Notifications/DataTransferObjects/NotificationObject.php new file mode 100644 index 00000000..40dc4f3c --- /dev/null +++ b/app/Classes/Modules/Notifications/DataTransferObjects/NotificationObject.php @@ -0,0 +1,101 @@ +title = $title; + $this->description = $description; + $this->subject = $subject; + $this->target = $target; + $this->causer = $causer; + $this->status = $status; + } + + /** + * @return int + */ + public function getTitle(): string + { + return $this->title; + } + + /** + * @return int + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * @return Notifiable + */ + public function getSubject(): Notifiable + { + return $this->subject; + } + + /** + * @return Notifiable + */ + public function getTarget(): Notifiable + { + return $this->target; + } + + /** + * @return Notifiable + */ + public function getCauser(): Notifiable + { + return $this->causer; + } + + /** + * @return int + */ + public function getStatus(): int + { + return $this->status; + } + + +} diff --git a/app/Classes/Modules/Notifications/Processors/CreateNotificationProcessor.php b/app/Classes/Modules/Notifications/Processors/CreateNotificationProcessor.php new file mode 100644 index 00000000..d6b077f8 --- /dev/null +++ b/app/Classes/Modules/Notifications/Processors/CreateNotificationProcessor.php @@ -0,0 +1,27 @@ +createsNotification = $createsNotification; + } + + public function execute(NotificationObject $object) + { + $notification = $this->createsNotification->execute($object); + return $notification; + } +} diff --git a/app/Classes/Modules/Notifications/Services/CreatesNotification.php b/app/Classes/Modules/Notifications/Services/CreatesNotification.php new file mode 100644 index 00000000..4f5ca5fd --- /dev/null +++ b/app/Classes/Modules/Notifications/Services/CreatesNotification.php @@ -0,0 +1,30 @@ +title = $object->getTitle(); + $model->description = $object->getDescription(); + $model->status = $object->getStatus(); + $model->subject_type = get_class($object->getSubject()); + $model->subject_id = $object->getSubject()->id; + $model->target_type = get_class($object->getTarget()); + $model->target_id = $object->getTarget()->id; + $model->causer_type = get_class($object->getCauser()); + $model->causer_id = $object->getCauser()->id; + $model->save(); + + return $model; + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Notifications/Services/ListsNotification.php b/app/Classes/Modules/Notifications/Services/ListsNotification.php new file mode 100644 index 00000000..707e7ae8 --- /dev/null +++ b/app/Classes/Modules/Notifications/Services/ListsNotification.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} \ No newline at end of file diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateLeadPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateLeadPerfexCRMObject.php new file mode 100644 index 00000000..bea178f5 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateLeadPerfexCRMObject.php @@ -0,0 +1,74 @@ +name = $name; + $this->email = $email; + $this->phone = $phone; + $this->companyName = $companyName; + $this->companyReference = $companyReference; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return string + */ + public function getEmail(): string + { + return $this->email; + } + + /** + * @return string + */ + public function getPhone(): string + { + return $this->phone; + } + + /** + * @return string + */ + public function getCompanyName(): string + { + return $this->companyName; + } + + /** + * @return string + */ + public function getCompanyReference(): string + { + return $this->companyReference; + } +} diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateTaskPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateTaskPerfexCRMObject.php new file mode 100644 index 00000000..2475ebbf --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateTaskPerfexCRMObject.php @@ -0,0 +1,173 @@ +email = $email; + $this->name = $name; + $this->description = $description; + $this->leadId = $leadId; + $this->projectId = $projectId; + $this->milestoneId = $milestoneId; + $this->reference = $reference; + $this->onTaskCompletion = $onTaskCompletion; + $this->status = $status; + $this->department = $department; + $this->priority = $priority; + $this->duedate = $duedate; + $this->invoiceId = $invoiceId; + } + + /** + * @return string + */ + public function getEmail(): string + { + return $this->email; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return string + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * @return string + */ + public function getLeadId(): string + { + return $this->leadId; + } + + public function setLeadId(string $leadId) + { + $this->leadId = $leadId; + } + + /** + * @return string + */ + public function getProjectId(): string + { + return $this->projectId; + } + + /** + * @return string + */ + public function getMilestoneId(): string + { + return $this->milestoneId; + } + + /** + * @return string + */ + public function getReference(): string + { + return $this->reference; + } + + /** + * @return string + */ + public function getOnTaskCompletion(): string + { + return $this->onTaskCompletion; + } + + /** + * @return string + */ + public function getStatus(): string + { + return $this->status; + } + + /** + * @return string + */ + public function getDepartment(): string + { + return $this->department; + } + + /** + * @return string + */ + public function getPriority(): string + { + return $this->priority; + } + + /** + * @return string + */ + public function getDuedate(): string + { + return $this->duedate; + } + + /** + * @return int + */ + public function getInvoiceId(): int + { + return $this->invoiceId; + } +} diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/CustomerContactObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CustomerContactObject.php new file mode 100644 index 00000000..3530524d --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CustomerContactObject.php @@ -0,0 +1,99 @@ +customerId = $customerId; + $this->firstname = $firstname; + $this->lastname = $lastname; + $this->email = $email; + $this->password = $password; + $this->isPrimary = $isPrimary; + $this->sendSetPasswordEmail = $sendSetPasswordEmail; + } + + /** + * @return int + */ + public function getCustomerId(): int + { + return $this->customerId; + } + + /** + * @return string + */ + public function getFirstName(): string + { + return $this->firstname; + } + + /** + * @return string + */ + public function getLastName(): string + { + return $this->lastname; + } + + /** + * @return string + */ + public function getEmail(): string + { + return $this->email; + } + + /** + * @return string + */ + public function getPassword(): string + { + return $this->password; + } + + /** + * @return string + */ + public function getIsPrimary(): string + { + return $this->isPrimary; + } + + /** + * @return string + */ + public function getSendSetPasswordEmail(): string + { + return $this->sendSetPasswordEmail; + } +} diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/FetchPerfexCRMInvoiceObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/FetchPerfexCRMInvoiceObject.php new file mode 100644 index 00000000..56c5a01d --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/FetchPerfexCRMInvoiceObject.php @@ -0,0 +1,38 @@ +email = $email; + $this->transaction = $transaction; + } + + /** + * @return string + */ + public function getEmail(): string + { + return $this->email; + } + + /** + * @return Transaction + */ + public function getTransaction(): Transaction + { + return $this->transaction; + } +} diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/InitialPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InitialPerfexCRMObject.php new file mode 100644 index 00000000..66c2c86c --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InitialPerfexCRMObject.php @@ -0,0 +1,124 @@ +companyName = $companyName; + $this->companyReference = $companyReference; + $this->contactName = $contactName; + $this->contactEmail = $contactEmail; + $this->bookingMarking = $bookingMarking; + $this->projectName = $projectName; + $this->projectStatus = $projectStatus; + $this->milestoneNames = $milestoneNames; + $this->taskNames = $taskNames; + } + + /** + * @return string + */ + public function getCompanyName(): string + { + return $this->companyName; + } + + /** + * @return string + */ + public function getCompanyReference(): string + { + return $this->companyReference; + } + + /** + * @return string + */ + public function getContactName(): string + { + return $this->contactName; + } + + /** + * @return string + */ + public function getContactEmail(): string + { + return $this->contactEmail; + } + + /** + * @return string + */ + public function getBookingMarking(): string + { + return $this->bookingMarking; + } + + /** + * @return string + */ + public function getProjectName(): string + { + return $this->projectName; + } + + /** + * @return string + */ + public function getProjectStatus(): int + { + return $this->projectStatus; + } + + /** + * @return array + */ + public function getMilestoneNames(): array + { + return $this->milestoneNames; + } + + /** + * @return array + */ + public function getTaskNames(): array + { + return $this->taskNames; + } + +} diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePaymentPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePaymentPerfexCRMObject.php new file mode 100644 index 00000000..3d34c540 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePaymentPerfexCRMObject.php @@ -0,0 +1,86 @@ +invoiceId = $invoiceId; + $this->amount = $amount; + $this->date = $date; + $this->paymentMode = $paymentMode; + $this->transactionId = $transactionId; + $this->note = $note; + } + + /** + * @return int + */ + public function getInvoiceId(): int + { + return $this->invoiceId; + } + + /** + * @return float + */ + public function getAmount(): float + { + return $this->amount; + } + + /** + * @return string + */ + public function getDate(): string + { + return $this->date; + } + + /** + * @return int + */ + public function getPaymentMode(): int + { + return $this->paymentMode; + } + + /** + * @return string + */ + public function getTransactionId(): string + { + return $this->transactionId; + } + + /** + * @return string + */ + public function getNote(): string + { + return $this->note; + } +} diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePerfexCRMObject.php new file mode 100644 index 00000000..f1f21f11 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePerfexCRMObject.php @@ -0,0 +1,145 @@ +clientId = $clientId; + $this->number = $number; + $this->date = $date; + $this->dueDate = $dueDate; + $this->currency = $currency; + $this->subTotal = $subTotal; + $this->total = $total; + $this->billingStreet = $billingStreet; + $this->projectId = $projectId; + $this->allowedPaymentModes = $allowedPaymentModes; + $this->invoiceItems = $invoiceItems; + } + + /** + * @return string + */ + public function getClientId(): string + { + return $this->clientId; + } + + /** + * @return string + */ + public function getNumber(): string + { + return $this->number; + } + + /** + * @return string + */ + public function getDate(): string + { + return $this->date; + } + + /** + * @return string + */ + public function getDueDate(): string + { + return $this->dueDate; + } + + /** + * @return string + */ + public function getCurrency(): string + { + return $this->currency; + } + + /** + * @return float + */ + public function getSubTotal(): float + { + return $this->subTotal; + } + + /** + * @return float + */ + public function getTotal(): float + { + return $this->total; + } + + /** + * @return string + */ + public function getBillingStreet(): string + { + return $this->billingStreet; + } + + /** + * @return string + */ + public function getProjectId(): string + { + return $this->projectId; + } + + /** + * @return array + */ + public function getAllowedPaymentModes(): array + { + return $this->allowedPaymentModes; + } + + /** + * @return array + */ + public function getInvoiceItems(): array + { + return $this->invoiceItems; + } +} diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoiceSingleItemPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoiceSingleItemPerfexCRMObject.php new file mode 100644 index 00000000..7b9428db --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoiceSingleItemPerfexCRMObject.php @@ -0,0 +1,86 @@ +description = $description; + $this->longDescription = $longDescription; + $this->qty = $qty; + $this->rate = $rate; + $this->order = $order; + $this->unit = $unit; + } + + /** + * @return string + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * @return string + */ + public function getLongDescription(): string + { + return $this->longDescription; + } + + /** + * @return float + */ + public function getQty(): float + { + return $this->qty; + } + + /** + * @return float + */ + public function getRate(): float + { + return $this->rate; + } + + /** + * @return int + */ + public function getOrder(): int + { + return $this->order; + } + + /** + * @return string + */ + public function getUnit(): string + { + return $this->unit; + } +} diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMInvoiceObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMInvoiceObject.php new file mode 100644 index 00000000..ad7fb921 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMInvoiceObject.php @@ -0,0 +1,74 @@ +email = $email; + $this->transaction = $transaction; + $this->isPaid = $isPaid; + $this->projectName = $projectName; + $this->projectId = $projectId; + } + + /** + * @return string + */ + public function getEmail(): string + { + return $this->email; + } + + /** + * @return Transaction + */ + public function getTransaction(): Transaction + { + return $this->transaction; + } + + /** + * @return bool + */ + public function getIsPaid(): bool + { + return $this->isPaid; + } + + /** + * @return string + */ + public function getProjectName(): string + { + return $this->projectName; + } + + /** + * @return string + */ + public function getProjectId(): string + { + return $this->projectId; + } +} diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMObject.php new file mode 100644 index 00000000..96bbc8bd --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMObject.php @@ -0,0 +1,146 @@ +companyName = $companyName; + $this->companyReference = $companyReference; + $this->contactName = $contactName; + $this->contactEmail = $contactEmail; + $this->bookingMarking = $bookingMarking; + $this->projectName = $projectName; + $this->projectStatus = $projectStatus; + $this->$invoiceId = $invoiceId; + $this->milestoneNames = $milestoneNames; + $this->tasks = $tasks; + } + + /** + * @return string + */ + public function getCompanyName(): string + { + return $this->companyName; + } + + /** + * @return string + */ + public function getCompanyReference(): string + { + return $this->companyReference; + } + + /** + * @return string + */ + public function getContactName(): string + { + return $this->contactName; + } + + /** + * @return string + */ + public function getContactEmail(): string + { + return $this->contactEmail; + } + + /** + * @return string + */ + public function getBookingMarking(): string + { + return $this->bookingMarking; + } + + /** + * @return string + */ + public function getProjectName(): string + { + return $this->projectName; + } + + /** + * @return int + */ + public function getProjectStatus(): int + { + return $this->projectStatus; + } + + /** + * @return int + */ + public function getInvoiceId(): int + { + return $this->invoiceId; + } + + /** + * @return array + */ + public function getMilestoneNames(): array + { + return $this->milestoneNames; + } + + /** + * @return array + */ + public function getTasks(): array + { + return $this->tasks; + } + + public function setTasks($tasks) + { + $this->tasks = $tasks; + } + + public function setInvoiceId($invoiceId) + { + $this->invoiceId = $invoiceId; + } + +} diff --git a/app/Classes/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php new file mode 100644 index 00000000..379caae7 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php @@ -0,0 +1,47 @@ +company->reference; + $companyName = $booking->company->name; + $employee = $booking->company->employees()->first(); + $contactEmail = $employee->email; + $contactName = $employee->name; + $bookingMarking = $booking->marking; + + $serviceTypeName = $booking->company->services()->where('id', $booking->service_id)->first()->name; + $projectName = 'Exchange | '.$serviceTypeName.' | '.$bookingMarking; + + $updatePerfexCRMObject = new UpdatePerfexCRMObject( + $companyName, + $companyReference, + $contactName, + $contactEmail, + $bookingMarking, + $projectName, + PerfexCRMProjectStatus::NOT_STARTED, + 0, + [], + [] + ); + + UpdatePerfexCRM::dispatch($updatePerfexCRMObject, null, null); + + return true; + } +} diff --git a/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMInvoiceProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMInvoiceProcessor.php new file mode 100644 index 00000000..4edb2ef5 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMInvoiceProcessor.php @@ -0,0 +1,227 @@ +createsPerfexCRMInvoice = $createsPerfexCRMInvoice; + $this->createsPerfexCRMInvoicePayment = $createsPerfexCRMInvoicePayment; + $this->convertsPerfexCRMLeadToCustomer = $convertsPerfexCRMLeadToCustomer; + $this->fetchesPerfexCRMProject = $fetchesPerfexCRMProject; + $this->createsPerfexCRMCustomerProject = $createsPerfexCRMCustomerProject; + $this->fetchesCompany = $fetchesCompany; + } + + /** + * @param $transaction + * @return null|object + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function execute($transaction) { + $booking = $transaction->booking; + $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]); + $purchaseOrder = $booking->transactions() + ->where('type', TransactionType::PURCHASE_ORDER) + ->complete() + ->first(); + + $clientId = ""; + $number = $transaction->bill_no; + + $prefix = "INV-"; + //This will remove the prefix if prefix already exist in the string + if (substr($number, 0, strlen($prefix)) == $prefix) { + $number = substr($number, strlen($prefix)); + } + + $number = 'EXC-'.$number; + $date = Carbon::parse($booking->created_at)->format('Y-m-d'); + $dueDate = Carbon::parse($booking->created_at)->format('Y-m-d'); + $currency = 1; + $subTotal = 0.00; + $total = 0.00; + + $billingStreet = ""; + $addresses = $supplier->addresses()->where('billing', '=', true)->first(); + + if ($addresses !== null) { + $billingStreet = $billingStreet.$addresses->street_one; + $billingStreet = $billingStreet.$addresses->street_two.','; + $billingStreet = $billingStreet.$addresses->district()->first()->name.','; + $billingStreet = $billingStreet.$addresses->postcode; + $billingStreet = $billingStreet.$addresses->state()->first()->name.','; + $billingStreet = $billingStreet.$addresses->country()->first()->name; + } + else{ + $billingStreet = "[Pending Billing Details by user]"; + } + + $allowedPaymentModes = []; + $invoiceItems = []; + + $firstSupplier = $supplier->employees()->first(); + $email = null; + if ($firstSupplier) { + $email = $firstSupplier->email; + Log::error('CreatePerfexCRMInvoiceProcessor debug:'.$email); + } else { + $bookingMarking = $transaction->owner->marking; + $serviceTypeName = $transaction->owner->company->services()->where('id', $transaction->owner->service_id)->first()->name; + $projectName = 'Exchange | '.$serviceTypeName.' | '.$bookingMarking; + Log::error('$projectName: '.$projectName); + return $email; + } + + $result = $this->convertsPerfexCRMLeadToCustomer->execute($email); + if(isset($result->payload)){ + $clientId = $result->payload['client_id']; + } + + //get project + $projectId = ""; + $bookingMarking = $transaction->owner->marking; + $serviceTypeName = $transaction->owner->company->services()->where('id', $transaction->owner->service_id)->first()->name; + $projectName = 'Exchange | '.$serviceTypeName.' | '.$bookingMarking; + $result = $this->fetchesPerfexCRMProject->execute($projectName, $clientId); + if(isset($result->payload)){ + $project = $result->payload[0]; + $projectId = $project['id']; + } + else{ + $result = $this->createsPerfexCRMCustomerProject->execute($projectName, PerfexCRMProjectStatus::NOT_STARTED, $clientId); + if(isset($result->payload)){ //Here means project creation successful + $projectId = $result->payload['project_id']; + } + } + + if(is_null($purchaseOrder)){ + $extraInvoiceSingleItem = new InvoiceSingleItemPerfexCRMObject( + 'Refer to booking: '.$booking->marking, + "", + 1.00, + $transaction->amount, + 1, + "" + ); + $subTotal += number_format($transaction->amount, 2,'.','') * 1; + array_push($invoiceItems, $extraInvoiceSingleItem); + } + else{ + foreach ($purchaseOrder->transactionDetails as $key => $transaction_detail){ + $order = $key + 1; + $stockCode = $transaction_detail->product_code; + $description = $transaction_detail->product_name; + $quantity = $transaction_detail->quantity; + $unitPrice = 0.00; + if($booking->first()->fix_currency_id !== 1) + $unitPrice = (1/$transaction->currency_rate) * $transaction_detail->price; + else + $unitPrice = $transaction_detail->price; + + //$totalAmount = 0.00; + if($booking->first()->fix_currency_id !== 1){ + + //$totalAmount = (float)number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity; + $subTotal += number_format((1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','') * $transaction_detail->quantity; + } + else + { + //$totalAmount = (float)number_format($transaction_detail->price, 2,'.','')*$transaction_detail->quantity; + $subTotal += number_format($transaction_detail->price, 2,'.','') * $transaction_detail->quantity; + } + + //string $description, string $longDescription, int $qty, int $rate, int $order, string $unit + $invoiceSingleItem = new InvoiceSingleItemPerfexCRMObject( + $description, + "", + $quantity, + $unitPrice, + $order, + "" + ); + array_push($invoiceItems, $invoiceSingleItem); + } + } + + //When this transaction is of TransactionType::PAYMENT, the amount is actually in the currecy user choose to pay (RM) + //So there is no need to convert it + if ($transaction->type == TransactionType::PAYMENT){ + $total = number_format($transaction->amount, 2,'.','') * 1; + } + else{ + if($booking->first()->fix_currency_id !== 1){ + $total = ((1/$transaction->currency_rate) * $transaction->amount) + $transaction->service_charge + $transaction->tax; + } + else{ + $total = $transaction->amount + $transaction->service_charge + $transaction->tax; + } + } + + //This setting is similar to Setup > Leads > Sources, Setup > Leads > Statuses + //Can be found at Finance > Payment Modes + array_push($allowedPaymentModes, 1, 2); + + $invoicePerfexCRMObject = new InvoicePerfexCRMObject( + $clientId, + $number, + $date, + $dueDate, + $currency, + $subTotal, + $total, + $billingStreet, + $projectId, + $allowedPaymentModes, + $invoiceItems + ); + + $result = $this->createsPerfexCRMInvoice->execute($invoicePerfexCRMObject); + return $result; + } +} + diff --git a/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMLeadProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMLeadProcessor.php new file mode 100644 index 00000000..5a0bc6dc --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMLeadProcessor.php @@ -0,0 +1,36 @@ +createsPerfexCRMLead = $createsPerfexCRMLead; + } + + /** + * @param CreateLeadPerfexCRMObject $createLeadPerfexCRMObject + * @return null|object + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function execute(CreateLeadPerfexCRMObject $createLeadPerfexCRMObject) { + return $this->createsPerfexCRMLead->execute($createLeadPerfexCRMObject); + } + +} diff --git a/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMTaskProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMTaskProcessor.php new file mode 100644 index 00000000..efd6df0e --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMTaskProcessor.php @@ -0,0 +1,32 @@ +createsPerfexCRMTask = $createsPerfexCRMTask; + } + + /** + * @param CreateTaskPerfexCRMObject $createTaskPerfexCRMObject + * @return true + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function execute(CreateTaskPerfexCRMObject $createTaskPerfexCRMObject) { + $this->createsPerfexCRMTask->execute($createTaskPerfexCRMObject); + return true; + } + +} diff --git a/app/Classes/Modules/PerfexCRM/Processors/FetchPerfexCRMInvoiceProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/FetchPerfexCRMInvoiceProcessor.php new file mode 100644 index 00000000..4c8ee9bf --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Processors/FetchPerfexCRMInvoiceProcessor.php @@ -0,0 +1,74 @@ +fetchesPerfexCRMCustomer = $fetchesPerfexCRMCustomer; + $this->fetchesPerfexCRMInvoice = $fetchesPerfexCRMInvoice; + $this->createPerfexCRMInvoiceProcessor = $createPerfexCRMInvoiceProcessor; + } + + public function execute(FetchPerfexCRMInvoiceObject $fetchPerfexCRMInvoiceObject) + { + //invoiceId to be returned - fetch or create + $invoiceId = 0; + + //get the client id + $customer = $this->fetchesPerfexCRMCustomer->execute($fetchPerfexCRMInvoiceObject->getEmail()); + $transaction = $fetchPerfexCRMInvoiceObject->getTransaction(); + + $number = $transaction->bill_no; + + $prefix = "INV-"; + //This will remove the prefix if prefix already exist in the string + if (substr($number, 0, strlen($prefix)) == $prefix) { + $number = substr($number, strlen($prefix)); + } + + $number = 'EXC-'.$number; + $invoice = $this->fetchesPerfexCRMInvoice->execute($customer->userid,"INV-", $number); + if(is_null($invoice)){ + $result = $this->createPerfexCRMInvoiceProcessor->execute($transaction); + if ($result) { + $invoiceId = $result->payload['id']; + } else { + $log['message'] = 'FetchPerfexCRMInvoiceProcessor failed for transaction > bill_no: '.$number; + Helper::debugLogger($log); + } + } + else{ + $invoiceId = $invoice->id; + } + + return $invoiceId; + } +} diff --git a/app/Classes/Modules/PerfexCRM/Processors/NewLeadTaskToPerfexCRMProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/NewLeadTaskToPerfexCRMProcessor.php new file mode 100644 index 00000000..2195fc46 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Processors/NewLeadTaskToPerfexCRMProcessor.php @@ -0,0 +1,43 @@ +employees()->first(); + $contactEmail = $employee->email; + + $createTaskPerfexCRMObject = new CreateTaskPerfexCRMObject( + $contactEmail, + PerfexCRMTasks::TASK_IDENTIFICATION_1['name'], + PerfexCRMTasks::TASK_IDENTIFICATION_1['description'], + "", + "", + "", + "", + "", + PerfexCRMTasks::TASK_IDENTIFICATION_1['status'], + PerfexCRMTasks::TASK_IDENTIFICATION_1['department'], + PerfexCRMTaskPriority::DEFAULT, + "0", + 0 + ); + + CreatePerfexCRMSingleTask::dispatch($createTaskPerfexCRMObject); + return true; + } + +} diff --git a/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessor.php new file mode 100644 index 00000000..b996e592 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessor.php @@ -0,0 +1,160 @@ +owner instanceof \App\Models\Booking) { + $companyReference = $model->owner->company->reference; + $companyName = $model->owner->company->name; + $employee = $model->owner->company->employees()->first(); + $contactEmail = $employee->email; + $contactName = $employee->name; + $bookingMarking = $model->owner->marking; + + $serviceTypeName = $model->owner->company->services()->where('id', $model->owner->service_id)->first()->name; + $projectName = 'Exchange | '.$serviceTypeName.' | '.$bookingMarking; + + if($status == ApprovalStatus::APPROVED) + { + if($model->type == TransactionType::PAYMENT){ + + $tasks = [ + PerfexCRMTasks::TASK_1 + ]; + if($model->owner->service_id == 1){ + $tasks = [ + PerfexCRMTasks::TASK_1, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_1, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_2, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_3, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_3_1, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_4, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_5, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_6, + ]; + } + else if($model->owner->service_id == 3){ + $tasks = [ + PerfexCRMTasks::TASK_1, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_1, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_2, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_3, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_3_1, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_4, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_5, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_6, + ]; + } + else if($model->owner->service_id == 4){ + $tasks = [ + PerfexCRMTasks::TASK_1, + PerfexCRMTasks::TASK_1688_PAYMENT_1, + PerfexCRMTasks::TASK_1688_PAYMENT_2, + PerfexCRMTasks::TASK_1688_PAYMENT_3, + PerfexCRMTasks::TASK_1688_PAYMENT_3_1, + PerfexCRMTasks::TASK_1688_PAYMENT_4, + PerfexCRMTasks::TASK_1688_PAYMENT_5, + PerfexCRMTasks::TASK_1688_PAYMENT_6, + PerfexCRMTasks::TASK_1688_PAYMENT_7, + PerfexCRMTasks::TASK_1688_PAYMENT_8, + PerfexCRMTasks::TASK_1688_PAYMENT_9, + PerfexCRMTasks::TASK_1688_PAYMENT_10, + PerfexCRMTasks::TASK_1688_PAYMENT_11, + PerfexCRMTasks::TASK_1688_PAYMENT_12, + PerfexCRMTasks::TASK_1688_PAYMENT_13, + + ]; + } + + //Add in tasks if customer has no purchase order yet and using 1 days or 3 days transfer service + if($model->owner->service_id == 1 || $model->owner->service_id == 3){ + $booking = $model->booking; + $purchaseOrder = $booking->transactions() + ->where('type', TransactionType::PURCHASE_ORDER) + ->complete() + ->first(); + if(is_null($purchaseOrder)){ + $potask1 = PerfexCRMTasks::TASK_PURCHASE_ORDER_1; + $potask1['status'] = PerfexCRMTaskStatus::NOT_STARTED; + $poTasks = [ + $potask1, + PerfexCRMTasks::TASK_PURCHASE_ORDER_2, + ]; + $tasks = array_merge($tasks, $poTasks); + } + } + + $updatePerfexCRMObject = new UpdatePerfexCRMObject( + $companyName, + $companyReference, + $contactName, + $contactEmail, + $bookingMarking, + $projectName, + PerfexCRMProjectStatus::IN_PROGRESS, + 0, + [], + $tasks + ); + + UpdatePerfexCRMPrelude::dispatch($model, $updatePerfexCRMObject, UpdatePerfexCRM::class, UpdatePerfexCRMInvoice::class); + } + } + else if($status == ApprovalStatus::PENDING_VERIFICATION){ + if($model->type == TransactionType::PURCHASE_ORDER){ + $tasks = []; + if($model->owner->service_id == 1 || $model->owner->service_id == 3){ + $tasks = [ + PerfexCRMTasks::TASK_PURCHASE_ORDER_1, + PerfexCRMTasks::TASK_PURCHASE_ORDER_2, + ]; + } + + if(count($tasks) > 0){ + $updatePerfexCRMObject = new UpdatePerfexCRMObject( + $companyName, + $companyReference, + $contactName, + $contactEmail, + $bookingMarking, + $projectName, + PerfexCRMProjectStatus::IN_PROGRESS, + 0, + [], + $tasks + ); + UpdatePerfexCRM::dispatch($updatePerfexCRMObject, null, null); + } + } + } + } + } + catch (\Exception $exception) { + Log::error('TransactionToPerfexCRMProcessor debug:'); + Log::error($exception); + } + return true; + } +} diff --git a/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessorV2.php b/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessorV2.php new file mode 100644 index 00000000..b9566a4f --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessorV2.php @@ -0,0 +1,246 @@ +type, [TransactionType::PAYMENT, TransactionType::BILL, TransactionType::PURCHASE_ORDER])) { + $transaction = ($model->type === TransactionType::BILL ? $model->owner : $model); + $bookingInfo = $this->extractBookingInfo($transaction); + $projectName = 'Exchange | ' . $bookingInfo['serviceTypeName'] . ' | ' . $bookingInfo['bookingMarking']; + $tasks = $this->defineTasks($model, $status); + + //Log::error("TransactionToPerfexCRMProcessorV2 status: ".$status." - for project name: ".$projectName); + + if(count($tasks) > 0) { + $updatePerfexCRMObject = new UpdatePerfexCRMObject( + $bookingInfo['companyName'], + $bookingInfo['companyReference'], + $bookingInfo['contactName'], + $bookingInfo['contactEmail'], + $bookingInfo['bookingMarking'], + $projectName, + PerfexCRMProjectStatus::IN_PROGRESS, + 0, + [], + $tasks + ); + $this->dispatchUpdateJob($transaction, $status, $updatePerfexCRMObject); + } + } + } catch (\Exception $exception) { + Log::error($exception); + } + return true; + } + + private function extractBookingInfo(Transaction $model): array + { + $bookingInfo = []; + $bookingInfo['companyReference'] = $model->owner->company->reference; + $bookingInfo['companyName'] = $model->owner->company->name; + $bookingInfo['contactEmail'] = $model->owner->company->employees()->first()->email; + $bookingInfo['contactName'] = $model->owner->company->employees()->first()->name; + $bookingInfo['bookingMarking'] = $model->owner->marking; + $bookingInfo['serviceTypeName'] = $model->owner->company->services()->where('id', $model->owner->service_id)->first()->name; + + return $bookingInfo; + } + + private function defineTasks(Transaction $model, int $status): array + { + $tasks = []; + $ownerServiceId = $model->type === TransactionType::BILL ? $model->owner->owner->service_id : $model->owner->service_id; + + if ($status === ApprovalStatus::PENDING_VERIFICATION) { + $tasks = $this->handlePendingVerificationStatus($model, $ownerServiceId); + } elseif ($status === ApprovalStatus::APPROVED) { + $tasks = $this->handleApprovedStatus($model, $ownerServiceId); + } + + return $tasks; + } + + private function handlePendingVerificationStatus(Transaction $model, int $serviceId): array + { + $tasks = []; + switch($model->type) { + case TransactionType::PAYMENT: + $tasks = $this->definePaymentTasks($model); + break; + case TransactionType::PURCHASE_ORDER: + if ($serviceId === 1 || $serviceId === 3) { + $task = PerfexCRMTasks::TASK_PURCHASE_ORDER_1; + $task['status'] = PerfexCRMTaskStatus::IN_PROGRESS; + $tasks = [$task]; + } + break; + case TransactionType::BILL: + $tasks = $this->handleBillPendingStatus($serviceId); + break; + } + + return $tasks; + } + + private function handleApprovedStatus(Transaction $model, int $serviceId): array + { + $tasks = []; + switch($model->type) { + case TransactionType::PAYMENT: + $tasks = $this->handlePaymentApprovedStatus($serviceId); + break; + case TransactionType::BILL: + if ($serviceId === 4) { + $tasks = $this->completeTask(PerfexCRMTasks::TASK_1688_PAYMENT_9, PerfexCRMTasks::TASK_1688_PAYMENT_10); + } + break; + } + + return $tasks; + } + + private function handlePaymentApprovedStatus(int $serviceId): array + { + $tasks = []; + switch ($serviceId) { + case 1: + $tasks = $this->completeTask(PerfexCRMTasks::TASK_1_DAY_TRANSFER_2, PerfexCRMTasks::TASK_1_DAY_TRANSFER_5); + break; + case 3: + $tasks = $this->completeTask(PerfexCRMTasks::TASK_3_DAY_TRANSFER_2, PerfexCRMTasks::TASK_3_DAY_TRANSFER_5); + break; + case 4: + $tasks = $this->completeTask(PerfexCRMTasks::TASK_1688_PAYMENT_2, PerfexCRMTasks::TASK_1688_PAYMENT_5); + break; + } + + return $tasks; + } + + private function handleBillPendingStatus(int $serviceId): array + { + $tasks = []; + switch ($serviceId) { + case 1: + $tasks = $this->completeTask(PerfexCRMTasks::TASK_1_DAY_TRANSFER_5, PerfexCRMTasks::TASK_1_DAY_TRANSFER_6); + break; + case 3: + $tasks = $this->completeTask(PerfexCRMTasks::TASK_3_DAY_TRANSFER_5, PerfexCRMTasks::TASK_3_DAY_TRANSFER_6); + break; + case 4: + $tasks = $this->completeTask(PerfexCRMTasks::TASK_1688_PAYMENT_5, PerfexCRMTasks::TASK_1688_PAYMENT_7); + break; + } + + return $tasks; + } + + private function definePaymentTasks(Transaction $model): array + { + $tasks = [ +// PerfexCRMTasks::TASK_1 + ]; + + if ($model->owner->service_id === 1) { + $tasks = array_merge($tasks, $this->oneDayTransferTasks()); + } elseif ($model->owner->service_id === 3) { + $tasks = array_merge($tasks, $this->threeDayTransferTasks()); + } elseif ($model->owner->service_id === 4) { + $tasks = array_merge($tasks, $this->payment1688Tasks()); + } + + if ($model->owner->service_id === 1 || $model->owner->service_id === 3) { + $purchaseOrder = $model->booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->complete()->first(); + if(is_null($purchaseOrder)){ + $tasks = array_merge($tasks, $this->purchaseOrderTasks()); + } + } + + return $tasks; + } + + private function oneDayTransferTasks(): array + { + return [ +// PerfexCRMTasks::TASK_1_DAY_TRANSFER_1, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_2, +// PerfexCRMTasks::TASK_1_DAY_TRANSFER_3, +// PerfexCRMTasks::TASK_1_DAY_TRANSFER_3_1, +// PerfexCRMTasks::TASK_1_DAY_TRANSFER_4, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_5, + PerfexCRMTasks::TASK_1_DAY_TRANSFER_6 + ]; + } + + private function threeDayTransferTasks(): array + { + return [ +// PerfexCRMTasks::TASK_3_DAY_TRANSFER_1, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_2, +// PerfexCRMTasks::TASK_3_DAY_TRANSFER_3, +// PerfexCRMTasks::TASK_3_DAY_TRANSFER_3_1, +// PerfexCRMTasks::TASK_3_DAY_TRANSFER_4, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_5, + PerfexCRMTasks::TASK_3_DAY_TRANSFER_6 + ]; + } + + private function payment1688Tasks(): array + { + return [ +// PerfexCRMTasks::TASK_1688_PAYMENT_1, + PerfexCRMTasks::TASK_1688_PAYMENT_2, +// PerfexCRMTasks::TASK_1688_PAYMENT_3, +// PerfexCRMTasks::TASK_1688_PAYMENT_3_1, +// PerfexCRMTasks::TASK_1688_PAYMENT_4, + PerfexCRMTasks::TASK_1688_PAYMENT_5, +// PerfexCRMTasks::TASK_1688_PAYMENT_6, + PerfexCRMTasks::TASK_1688_PAYMENT_7, + PerfexCRMTasks::TASK_1688_PAYMENT_8, + PerfexCRMTasks::TASK_1688_PAYMENT_9, + PerfexCRMTasks::TASK_1688_PAYMENT_10, + PerfexCRMTasks::TASK_1688_PAYMENT_11, + PerfexCRMTasks::TASK_1688_PAYMENT_12, +// PerfexCRMTasks::TASK_1688_PAYMENT_13 + ]; + } + + private function purchaseOrderTasks(): array + { + $potask1 = PerfexCRMTasks::TASK_PURCHASE_ORDER_1; + $potask1['status'] = PerfexCRMTaskStatus::NOT_STARTED; + + return [ + $potask1, +// PerfexCRMTasks::TASK_PURCHASE_ORDER_2 + ]; + } + + private function dispatchUpdateJob(Transaction $model, int $status, UpdatePerfexCRMObject $updatePerfexCRMObject) + { + $withInvoice = ($model->type === TransactionType::PAYMENT && $model->owner instanceof Booking && $status === ApprovalStatus::APPROVED); + UpdatePerfexCRMPrelude::dispatch($model, $updatePerfexCRMObject, $withInvoice); + } + + private function completeTask($startTask, $endTask) { + $startTask['status'] = PerfexCRMTaskStatus::COMPLETED; + $endTask['status'] = PerfexCRMTaskStatus::IN_PROGRESS; + + return [$startTask, $endTask]; + } +} diff --git a/app/Classes/Modules/PerfexCRM/Processors/UpdatePerfexCRMProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/UpdatePerfexCRMProcessor.php new file mode 100644 index 00000000..fbf64be8 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Processors/UpdatePerfexCRMProcessor.php @@ -0,0 +1,256 @@ +convertsPerfexCRMLeadToCustomer = $convertsPerfexCRMLeadToCustomer; + $this->createsPerfexCRMCustomerProject = $createsPerfexCRMCustomerProject; + $this->createsPerfexCRMMilestone = $createsPerfexCRMMilestone; + $this->createsPerfexCRMTask = $createsPerfexCRMTask; + $this->fetchesPerfexCRMProject = $fetchesPerfexCRMProject; + $this->fetchesPerfexCRMMilestone = $fetchesPerfexCRMMilestone; + $this->fetchesPerfexCRMTask = $fetchesPerfexCRMTask; + $this->createsPerfexCRMCustomer = $createsPerfexCRMCustomer; + $this->createsPerfexCRMCustomerContact = $createsPerfexCRMCustomerContact; + $this->updatesPerfexCRMTask = $updatesPerfexCRMTask; + $this->updatesPerfexCRMCustomer = $updatesPerfexCRMCustomer; + $this->updatesPerfexCRMProject = $updatesPerfexCRMProject; + } + + /** + * @param UpdatePerfexCRMObject $updatePerfexCRMObject + * @return null|object + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function execute(UpdatePerfexCRMObject $updatePerfexCRMObject) { + $projectId = ""; + + // Customer has to exist first before Project can appear under it + // Check with Perfex CRM, if this user (email) was previously a lead, should automatically now become a customer + $crmCompany = $updatePerfexCRMObject->getCompanyName(); + $result = $this->convertsPerfexCRMLeadToCustomer->execute($updatePerfexCRMObject->getContactEmail()); + + if(isset($result->payload)){ + $crmClientId = $result->payload['client_id']; + if (isset($result->payload['company'])) { + $crmCompany = $result->payload['company']; + } + } + else{ + //if reach this point, this means this user is not a official customer nor is a lead in crm + //Create Customer has 2 parts: Create Company (client), Create Contact + $result = $this->createsPerfexCRMCustomer->execute($crmCompany); + if(is_null($result)){ + $crmCompany = $crmCompany." 2"; + $result = $this->createsPerfexCRMCustomer->execute($crmCompany); + } + $crmClientId = $result->payload['clientId']; + + $customerContactObject = new CustomerContactObject( + $crmClientId, + $updatePerfexCRMObject->getContactName(), + $updatePerfexCRMObject->getContactName(), + $updatePerfexCRMObject->getContactEmail(), + "pU^T@sC#9Q", + "on", + "on" + ); + $result = $this->createsPerfexCRMCustomerContact->execute($customerContactObject); + } + + //Update custom fields to identify company reference from exchange or shipping portal + $value_exists = false; + if (isset($result->payload['customfields'])) { + foreach ($result->payload['customfields'] as $element) { + if ($element['value'] === $updatePerfexCRMObject->getCompanyReference()) { + $value_exists = true; + break; + } + } + } + if(!$value_exists); + { + $result = $this->updatesPerfexCRMCustomer->execute($crmClientId, $crmCompany, $updatePerfexCRMObject->getCompanyReference()); + } + + // Get existing or create project, project has to exist first before milestone can appear under it + $result = $this->fetchesPerfexCRMProject->execute($updatePerfexCRMObject->getProjectName(), $crmClientId); + if(isset($result->payload)){ + $project = $result->payload[0]; + $projectId = $project['id']; + if($updatePerfexCRMObject->getProjectStatus() != PerfexCRMProjectStatus::NOT_STARTED && $project['status'] == PerfexCRMProjectStatus::NOT_STARTED) + { + $this->updatesPerfexCRMProject->execute($project, $updatePerfexCRMObject->getProjectStatus()); + } + } + else{ + $result = $this->createsPerfexCRMCustomerProject->execute($updatePerfexCRMObject->getProjectName(), $updatePerfexCRMObject->getProjectStatus(), $crmClientId); + if(isset($result->payload)){ //Here means project creation successful + $projectId = $result->payload['project_id']; + } + } + + $tasks = $updatePerfexCRMObject->getTasks(); + if(count($tasks) > 0){ + + //Create tasks with milestone + for($count=0; $count < count($tasks); $count++) { + $milestoneId = 0; //By default milestoneId is 0, having this set at individual task is optional + if($tasks[$count]['milestone'] != "") //Create milestone only if it is defined + { + // Get existing or create milestone, milestone has to exist first before task can appear under it + $result = $this->fetchesPerfexCRMMilestone->execute($tasks[$count]['milestone'], $projectId); + if(isset($result->payload)){ + $array = json_decode(json_encode($result->payload[0]), true); + $milestoneId = $array['id']; + } + else{ + $result = $this->createsPerfexCRMMilestone->execute($tasks[$count]['milestone'], $projectId, $count); + if(isset($result->payload)){ + $milestoneId = $result->payload['milestone_id']; + } + } + } + + $taskStatus = PerfexCRMTaskStatus::NOT_STARTED; + if($tasks[$count]['status'] != ''){ + $taskStatus = $tasks[$count]['status']; + } + + // Get existing or create task + $taskName = $tasks[$count]['name']; + $taskReference = $tasks[$count]['reference']; + $taskOnTaskCompletion = $tasks[$count]['on_task_completion']; + $taskIsAllowMultiple = $tasks[$count]['is_allow_multiple']; + $taskIsOnTaskCompletionUpdate = $tasks[$count]['is_on_task_completion_update']; + if($updatePerfexCRMObject->getInvoiceId() != 0 && $taskIsAllowMultiple){ + $taskName = $taskName." (".$updatePerfexCRMObject->getInvoiceId().")"; + $taskReference = $taskReference."_".$updatePerfexCRMObject->getInvoiceId(); + if($taskOnTaskCompletion && $taskIsOnTaskCompletionUpdate){ + $taskOnTaskCompletion = $taskOnTaskCompletion."_".$updatePerfexCRMObject->getInvoiceId(); + } + } + + $result = $this->fetchesPerfexCRMTask->execute($taskName, $milestoneId, 'project', $projectId, $updatePerfexCRMObject->getInvoiceId()); + // Log::error("UpdatePerfexCRMProcessor task: ".$taskName." , ".json_encode($result)); + Log::error("UpdatePerfexCRMProcessor task: ".$taskName); + + if(isset($result->payload)){ + //&& $result->payload[0]['status'] == PerfexCRMTaskStatus::NOT_STARTED + if($taskStatus != PerfexCRMTaskStatus::NOT_STARTED) + { + $task = $result->payload[0]; + $result = $this->updatesPerfexCRMTask->execute($task['id'], $task['name'], $task['milestone'], $task['rel_id'], $taskStatus, $task['startdate'], is_null($task['duedate']) ? '': $task['duedate']); + } + } + else{ + $createTaskPerfexCRMObject = new CreateTaskPerfexCRMObject( + "", + $taskName, + $tasks[$count]['description'], + "", + $projectId, + $milestoneId, + $taskReference, + $taskOnTaskCompletion, + $taskStatus, + $tasks[$count]['department'], + $tasks[$count]['priority'], + $tasks[$count]['duedate'], + $updatePerfexCRMObject->getInvoiceId() + ); + $result = $this->createsPerfexCRMTask->execute($createTaskPerfexCRMObject); + } + } + } + + $payload = []; + $payload['projectId'] = $projectId; + return (object) $payload; + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/ConvertsPerfexCRMLeadToCustomer.php b/app/Classes/Modules/PerfexCRM/Services/ConvertsPerfexCRMLeadToCustomer.php new file mode 100644 index 00000000..e6e7323e --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/ConvertsPerfexCRMLeadToCustomer.php @@ -0,0 +1,34 @@ + config('perfexcrm.api_key'),]) + ->get(config('perfexcrm.base_url').'/api/leads/convertocustomer/'.$email); + + if($response->successful()){ + $data = $response->json(); + + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomer.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomer.php new file mode 100644 index 00000000..e5a19ce3 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomer.php @@ -0,0 +1,37 @@ + $companyName + ]; + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/customers',$data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerContact.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerContact.php new file mode 100644 index 00000000..519c1495 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerContact.php @@ -0,0 +1,44 @@ + $customerContactObject->getCustomerId(), + 'firstname' => $customerContactObject->getFirstName(), + 'lastname' => $customerContactObject->getLastName(), + 'email' => $customerContactObject->getEmail(), //$email + 'password' => $customerContactObject->getPassword(), + 'is_primary' => $customerContactObject->getIsPrimary(), + //'send_set_password_email' => $customerContactObject->getSendSetPasswordEmail(), + ]; + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/contacts',$data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerProject.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerProject.php new file mode 100644 index 00000000..f5d4de3e --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerProject.php @@ -0,0 +1,43 @@ + $projectName, + 'rel_type' => 'customer', + 'billing_type' => 1, + 'clientid' => $clientId, + 'start_date' => date('Y-m-d'), + 'status' => $status + ]; + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/projects',$data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoice.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoice.php new file mode 100644 index 00000000..ba0c2755 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoice.php @@ -0,0 +1,63 @@ +getSubTotal() * 100) / 100; + $total = floor($invoicePerfexCRMObject->getTotal() * 100) / 100; + + $data = [ + 'clientid' => $invoicePerfexCRMObject->getClientId(), + 'number' => $invoicePerfexCRMObject->getNumber(), + 'date' => $invoicePerfexCRMObject->getDate(), + 'duedate' => $invoicePerfexCRMObject->getDueDate(), + 'currency' => $invoicePerfexCRMObject->getCurrency(), + 'subtotal' => number_format($subtotal, 2, '.', ''), + 'total' => number_format($total, 2, '.', ''), + 'billing_street' => $invoicePerfexCRMObject->getBillingStreet(), + 'project_id' => $invoicePerfexCRMObject->getProjectId(), + 'allowed_payment_modes[0]' => 1, + 'allowed_payment_modes[1]' => 2, + ]; + + for($count=0; $count < count($invoicePerfexCRMObject->getInvoiceItems()); $count++) { + $oneItem = [ + "newitems[".$count."][description]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->description, + "newitems[".$count."][long_description]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->longDescription, + "newitems[".$count."][qty]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->qty, + "newitems[".$count."][rate]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->rate, + "newitems[".$count."][order]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->order, + "newitems[".$count."][unit]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->unit, + ]; + $data = array_merge($data, $oneItem); + } + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/invoices',$data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoicePayment.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoicePayment.php new file mode 100644 index 00000000..f3e92529 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoicePayment.php @@ -0,0 +1,43 @@ + $invoicePaymentPerfexCRMObject->getInvoiceId(), + 'amount' => number_format($invoicePaymentPerfexCRMObject->getAmount(), 2, '.', ''), + 'date' => $invoicePaymentPerfexCRMObject->getDate(), + 'paymentmode' => $invoicePaymentPerfexCRMObject->getPaymentMode(), + 'transactionid' => $invoicePaymentPerfexCRMObject->getTransactionId(), + 'note' => $invoicePaymentPerfexCRMObject->getNote(), + ]; + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/invoices/recordpayment',$data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMLead.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMLead.php new file mode 100644 index 00000000..373f4171 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMLead.php @@ -0,0 +1,50 @@ + [ + 3 => $createLeadPerfexCRMObject->getCompanyReference() + ] + ]; + + $data = [ + 'name' => $createLeadPerfexCRMObject->getName(), + 'email' => $createLeadPerfexCRMObject->getEmail(), + 'phonenumber' => $createLeadPerfexCRMObject->getPhone(), + 'company' => $createLeadPerfexCRMObject->getCompanyName(), + 'source' => 1, //1: Exchange, 2: Shipping Portal + 'status' => 2, //2: Lead, 1: Customer, + 'custom_fields' => $custom_fields + ]; + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/leads/byemail',$data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMMilestone.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMMilestone.php new file mode 100644 index 00000000..80cb49bf --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMMilestone.php @@ -0,0 +1,46 @@ + $milestoneName, + 'project_id' => $projectId, + 'due_date' => date('Y-m-d'), + 'start_date' => date('Y-m-d') + ]; + + if($milestoneOrder != ""){ + $data['milestone_order'] = $milestoneOrder; + } + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/milestones',$data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMTask.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMTask.php new file mode 100644 index 00000000..feb8f9c8 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMTask.php @@ -0,0 +1,66 @@ +getDepartment() != ""){ + $custom_fields = [ + "tasks" => [ + PerfexCRMCustomFields::TASKS_DEPARTMENT => $createTaskPerfexCRMObject->getDepartment() + ] + ]; + } + + $data = [ + 'name' => $createTaskPerfexCRMObject->getName(), + 'description' => $createTaskPerfexCRMObject->getDescription(), + 'milestone' => $createTaskPerfexCRMObject->getMilestoneId(), + 'startdate' => date('Y-m-d'), + 'rel_type' => 'project', + 'rel_id' => $createTaskPerfexCRMObject->getProjectId(), + 'status' => $createTaskPerfexCRMObject->getStatus(), + 'is_system_created' => 1, + 'reference' => $createTaskPerfexCRMObject->getReference(), + 'on_task_completion' => $createTaskPerfexCRMObject->getOnTaskCompletion(), + 'custom_fields' => $custom_fields, + 'priority' => $createTaskPerfexCRMObject->getPriority(), + 'duedate' => date('Y-m-d', strtotime('+' . $createTaskPerfexCRMObject->getDuedate() . ' days')), + 'invoice_id' => $createTaskPerfexCRMObject->getInvoiceId(), + ]; + + if($createTaskPerfexCRMObject->getLeadId() != '') { + $data['rel_type'] = 'lead'; + $data['rel_id'] = $createTaskPerfexCRMObject->getLeadId(); + } + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/tasks',$data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMCustomer.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMCustomer.php new file mode 100644 index 00000000..9f3928f6 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMCustomer.php @@ -0,0 +1,34 @@ + config('perfexcrm.api_key'),]) + ->get(config('perfexcrm.base_url').'/api/customers/byemail/'.$email); + + if($response->successful()){ + $data = $response->json(); + + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMInvoice.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMInvoice.php new file mode 100644 index 00000000..cd7aff30 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMInvoice.php @@ -0,0 +1,36 @@ + config('perfexcrm.api_key'),]) + ->get(config('perfexcrm.base_url').'/api/invoices/customsearch/'.$clientId.'/'.$invoicePrefix.'/'.$invoiceNumber); + + if($response->successful()){ + $data = $response->json(); + + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMLead.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMLead.php new file mode 100644 index 00000000..df9c754d --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMLead.php @@ -0,0 +1,34 @@ + config('perfexcrm.api_key'),]) + ->get(config('perfexcrm.base_url').'/api/leads/byemail/'.$email); + + if($response->successful()){ + $data = $response->json(); + + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMMilestone.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMMilestone.php new file mode 100644 index 00000000..30ae0737 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMMilestone.php @@ -0,0 +1,40 @@ + $milestoneName, + 'project_id' => $projectId, + ]; + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/milestones/bynameandprojectid', $data); + + if($response->successful()){ + $data = $response->json(); + + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMProject.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMProject.php new file mode 100644 index 00000000..c55f2c01 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMProject.php @@ -0,0 +1,40 @@ + $projectName, + 'clientid' => $clientId, + ]; + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/projects/bynameandclientid', $data); + + if($response->successful()){ + $data = $response->json(); + + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php new file mode 100644 index 00000000..6cd4f052 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php @@ -0,0 +1,54 @@ + $taskName, + 'rel_type' => $relType, + 'rel_id' => $relId, + ]; + + if($milestoneId != '') { + $newItem = ['milestone' => $milestoneId ]; + $data = array_merge($data, $newItem); + } + + if($invoiceId != 0) { + $newItem = ['invoice_id' => $invoiceId ]; + $data = array_merge($data, $newItem); + } + + $response = Http::asForm()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->post(config('perfexcrm.base_url').'/api/tasks/customsearch', $data); + + if($response->successful()){ + $data = $response->json(); + + return (object) $data; + }else{ + Helper::debugLogger($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php new file mode 100644 index 00000000..6d91c081 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php @@ -0,0 +1,48 @@ + [ + PerfexCRMCustomFields::CUSTOMERS_EXCHANGE_REFERENCE => $companyReference + ] + ]; + + $data = [ + 'company' => $companyName, + 'custom_fields' => $custom_fields + ]; + + $response = Http::asJson()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->put(config('perfexcrm.base_url').'/api/customers/'.$customerId, $data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Helper::debugLogger($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMInvoice.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMInvoice.php new file mode 100644 index 00000000..9adb9460 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMInvoice.php @@ -0,0 +1,70 @@ +The Invoice number field is required.<\/p> + //

The Invoice date field is required.<\/p> + //

The Currency field is required.<\/p> + //

The Items field is required.<\/p> + //

The Allow Payment Mode field is required.<\/p> + //

The Billing Street field is required.<\/p> + //

The Sub Total field is required.<\/p> + //

The Total field is required.<\/p> + + $newInvoiceItems = []; + foreach ($invoice->items as $item) { + $item['itemid'] = $item['id']; + unset($item['id']); + $item['order'] = $item['item_order']; + unset($item['item_order']); + array_push($newInvoiceItems,$item); + } + + $allowedPaymentModes = []; + array_push($allowedPaymentModes, 1, 2); + + $data = [ + 'number' => $invoice->number, + 'date' => $invoice->date, + 'duedate' => $invoice->duedate, + 'currency' => $invoice->currency, + 'subtotal' => $invoice->subtotal, + 'total' => $invoice->total, + 'billing_street' => $invoice->billing_street, + 'shipping_street' => $invoice->billing_street, + 'project_id' => $projectId, + 'items' => $newInvoiceItems, + 'allowed_payment_modes' => $allowedPaymentModes, + ]; + + $response = Http::asJson()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->put(config('perfexcrm.base_url').'/api/invoices/'.$invoice->id, $data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMLead.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMLead.php new file mode 100644 index 00000000..f8fbc944 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMLead.php @@ -0,0 +1,51 @@ + [ + PerfexCRMCustomFields::LEADS_EXCHANGE_REFERENCE => $companyReference + ] + ]; + + $data = [ + 'name' => $lead->name, + 'email' => $lead->email, + 'phonenumber' => $lead->phonenumber, + 'company' => $lead->company, + 'custom_fields' => $custom_fields, + 'source' => $lead->source, + 'status' => $lead->status, + ]; + + $response = Http::asJson()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->put(config('perfexcrm.base_url').'/api/leads/'.$lead->id, $data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMProject.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMProject.php new file mode 100644 index 00000000..cf782806 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMProject.php @@ -0,0 +1,43 @@ + $project['name'], + 'clientid' => $project['clientid'], + // 'rel_type' => 'customer', + 'billing_type' => 1, + 'start_date' => $project['start_date'], + 'status' => $status + ]; + + $response = Http::asJson()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->put(config('perfexcrm.base_url').'/api/projects/'.$project['id'], $data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage()); + } + } +} diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMTask.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMTask.php new file mode 100644 index 00000000..95a09525 --- /dev/null +++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMTask.php @@ -0,0 +1,50 @@ + $taskName, + 'milestone' => $milestoneId, + 'startdate' => $startDate, + 'duedate' => $dueDate, + 'rel_type' => 'project', + 'rel_id' => $projectId, + 'status' => $status, + 'repeat_every' => '', + ]; + + $response = Http::asJson()->withHeaders([ + 'authtoken' => config('perfexcrm.api_key')]) + ->put(config('perfexcrm.base_url').'/api/tasks/'.$taskId, $data); + + if($response->successful()){ + $data = $response->json(); + return (object) $data; + }else{ + Log::error($response); + return null; + } + }catch(\Exception $exception){ + throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage()); + } + } +} 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/Segments/ControllersLogic/CreateSegmentLogic.php b/app/Classes/Modules/Segments/ControllersLogic/CreateSegmentLogic.php index 32eeb217..f00f8996 100644 --- a/app/Classes/Modules/Segments/ControllersLogic/CreateSegmentLogic.php +++ b/app/Classes/Modules/Segments/ControllersLogic/CreateSegmentLogic.php @@ -54,7 +54,7 @@ class CreateSegmentLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { - $segment_object = new SegmentObject($request->input('name')); + $segment_object = new SegmentObject($request->input('name'), $request->input('type') ?? 2); $this->canCreateSegment->passes($segment_object); $segment = $this->createsSegment->execute($segment_object); diff --git a/app/Classes/Modules/Segments/DataTransferObjects/SeasonalSegmentObject.php b/app/Classes/Modules/Segments/DataTransferObjects/SeasonalSegmentObject.php new file mode 100644 index 00000000..d045556e --- /dev/null +++ b/app/Classes/Modules/Segments/DataTransferObjects/SeasonalSegmentObject.php @@ -0,0 +1,74 @@ +companyId = $companyId; + $this->segmentId = $segmentId; + $this->starting_on = $starting_on; + $this->ending_on = $ending_on; + $this->status = $status; + } + + /** + * @return int + */ + public function getCompanyId(): int + { + return $this->companyId; + } + + /** + * @return int + */ + public function getSegmentId(): int + { + return $this->segmentId; + } + + + /** + * @return string + */ + public function getStartingOn(): string + { + return $this->starting_on; + } + + /** + * @return string + */ + public function getEndingOn(): ?string + { + return $this->ending_on; + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Segments/Processors/RiskAnalysisProcessor.php b/app/Classes/Modules/Segments/Processors/RiskAnalysisProcessor.php new file mode 100644 index 00000000..66cf3a93 --- /dev/null +++ b/app/Classes/Modules/Segments/Processors/RiskAnalysisProcessor.php @@ -0,0 +1,37 @@ +assignSegmentProcessor = $assignSegmentProcessor; + } + + + function execute(User $user, string $token){ + $captchaToken = GoogleReCaptchaV3::verifyResponse($token); + + if($captchaToken->getScore() < 0.6 && !in_array('timeout-or-duplicate', $captchaToken->getErrorCodes())){ + $this->assignSegmentProcessor->execute($user->company()->first(), 18); + } + } + +} \ No newline at end of file diff --git a/app/Classes/Modules/Segments/Services/CreatesSeasonalSegment.php b/app/Classes/Modules/Segments/Services/CreatesSeasonalSegment.php new file mode 100644 index 00000000..0125a956 --- /dev/null +++ b/app/Classes/Modules/Segments/Services/CreatesSeasonalSegment.php @@ -0,0 +1,27 @@ +company_id = $object->getCompanyId(); + $model->segment_id = $object->getSegmentId(); + $model->starting_on = $object->getStartingOn(); + $model->ending_on = $object->getEndingOn(); + + return $this->handler($model); + + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Segments/Services/CreatesSegment.php b/app/Classes/Modules/Segments/Services/CreatesSegment.php index b18a8db5..0c2491d9 100644 --- a/app/Classes/Modules/Segments/Services/CreatesSegment.php +++ b/app/Classes/Modules/Segments/Services/CreatesSegment.php @@ -4,7 +4,6 @@ namespace App\Classes\Modules\Segments\Services; use App\Classes\General\Eloquent\AbstractUpdateRecord; use App\Classes\Modules\Segments\DataTransferObjects\SegmentObject; -use App\Classes\ValueObjects\Constants\SegmentConstants; use App\Models\Segment; class CreatesSegment extends AbstractUpdateRecord @@ -17,7 +16,7 @@ class CreatesSegment extends AbstractUpdateRecord public function execute(SegmentObject $object) { $model = new Segment(); $model->name = $object->getName(); - $model->type = SegmentConstants::CUSTOM_SEGMENT; + $model->type = $object->getType(); return $this->handler($model); diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderDocumentLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderDocumentLogic.php new file mode 100644 index 00000000..297d275b --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderDocumentLogic.php @@ -0,0 +1,48 @@ + 'Generate Bulk Purchase Order', + 'message' => 'You have successfully generated bulk purchase order' + ]; + } + + /** @var GenerateGroupTransactionsPurchaseOrder */ + private $generateGroupTransactionsPurchaseOrder; + + /** + * CreateBulkPurchaseOrderDocumentLogic constructor. + * @param GenerateGroupTransactionsPurchaseOrder $generateGroupTransactionsPurchaseOrder + */ + public function __construct(GenerateGroupTransactionsPurchaseOrder $generateGroupTransactionsPurchaseOrder) + { + $this->generateGroupTransactionsPurchaseOrder = $generateGroupTransactionsPurchaseOrder; + } + + /** + * @param Request $request + * @return JsonResponse + */ + public function logic(Request $request): JsonResponse + { + $this->generateGroupTransactionsPurchaseOrder::dispatch(); + + return $this->response([]); + } +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php new file mode 100644 index 00000000..3771c559 --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php @@ -0,0 +1,100 @@ + 'Generate Bulk Purchase Order', + 'message' => 'You have successfully generated bulk purchase order' + ]; + } + + /** @var ListsGroups */ + private $listsGroups; + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** @var CreateInvoiceDocumentProcessor */ + private $invoiceDocumentProcessor; + + /** + * CreateBulkPurchaseOrderTransactionLogic constructor. + * @param ListsGroups $listsGroups + * @param FetchesCompany $fetchesCompany + * @param CreateInvoiceDocumentProcessor $invoiceDocumentProcessor + */ + public function __construct(ListsGroups $listsGroups, FetchesCompany $fetchesCompany, CreateInvoiceDocumentProcessor $invoiceDocumentProcessor) + { + $this->listsGroups = $listsGroups; + $this->fetchesCompany = $fetchesCompany; + $this->invoiceDocumentProcessor = $invoiceDocumentProcessor; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $groups = $this->listsGroups->execute(['issuer_id' => [$request->issuer_id], 'date_start' => $request->start_date, 'date_end' => $request->end_date]); + + foreach($groups as $group){ + foreach($group->transactions as $transaction){ + if($transaction->owner()->owner()->transactions()->where('type', TransactionType::PURCHASE_ORDER)->where('status', '!=', ApprovalStatus::APPROVED)->exists()){ + throw new MalformedRequestException('You can\'t generate bulk purchased order if there in uncomplete transactions'); + } + } + } + + foreach($groups as $group){ + foreach($group->transactions as $transaction){ + $completed_transactions = $transaction->owner()->owner()->transactions()->where('type', TransactionType::PURCHASE_ORDER)->where('status', '=', ApprovalStatus::APPROVED)->get(); + + $purchaseOrder = $transaction->owner()->transactions() + ->where('type', TransactionType::PURCHASE_ORDER) + ->complete() + ->first(); + + 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, $voucherRedemption); + } + } + } + + return $this->response([]); + + } + + + +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php index 460f5181..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,20 +51,25 @@ class CreatePaymentProofDocumentLogic extends AbstractControllerLogic /** @var CreateInvoiceTransactionProcessor */ private $createInvoiceTransactionProcessor; + /** @var SendUserPaymentProofUploadedEmail */ + private $sendUserPaymentProofUploadedEmail; + /** - * CreatePaymentVerificationDocumentLogic constructor. + * CreatePaymentProofDocumentLogic constructor. * @param FetchesTransaction $fetchesTransaction * @param CreatesDocument $createsDocument * @param CreatesFiles $createsFile * @param UpdatesTransactionStatus $updatesTransactionStatus + * @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor */ - public function __construct(FetchesTransaction $fetchesTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesTransactionStatus $updatesTransactionStatus, CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor) + 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; } /** @@ -81,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/CreateSupplierTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php index 1cf71643..2dcbd0f7 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php @@ -4,16 +4,18 @@ namespace App\Classes\Modules\Transactions\ControllersLogic; use App\Classes\Modules\Transactions\Processors\CreateSupplierTransactionProcessor; +use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber; use App\Models\Document; +use App\Models\Group; + use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; -use Meneses\LaravelMpdf\Facades\LaravelMpdf; +use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf; use App\Classes\ValueObjects\Constants\DocumentType; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\Modules\Documents\Services\CreatesFiles; use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Companies\Services\FetchesCompany; -use Meneses\LaravelLaravelMpdf\Facades\LaravelLaravelMpdf; use App\Classes\Modules\Documents\Services\CreatesDocument; use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; @@ -43,19 +45,25 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic /** @var CreatesFiles */ private $createsFile; + /** @var GeneratesTransactionBillNumber */ + private $generatesTransactionBillNumber; + + /** * CreateSupplierTransactionLogic constructor. * @param FetchesCompany $fetchesCompany * @param CreateSupplierTransactionProcessor $createSupplierTransactionProcessor * @param CreatesDocument $createsDocument * @param CreatesFiles $createsFile + * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber */ - public function __construct(FetchesCompany $fetchesCompany, CreateSupplierTransactionProcessor $createSupplierTransactionProcessor, CreatesDocument $createsDocument, CreatesFiles $createsFile) + public function __construct(FetchesCompany $fetchesCompany, CreateSupplierTransactionProcessor $createSupplierTransactionProcessor, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber) { $this->fetchesCompany = $fetchesCompany; $this->createSupplierTransactionProcessor = $createSupplierTransactionProcessor; $this->createsDocument = $createsDocument; $this->createsFile = $createsFile; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; } public function logic(Request $request) : JsonResponse @@ -71,6 +79,45 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic if(!count($this->createSupplierTransactionProcessor->getBills())) return $this->response([]); + $group = new Group(); + $group->save(); + + $issuer = ''; + $receiver = ''; + $amount = 0; + $original_amount = 0; + $currency_id = 0; + $original_currency_id = ''; + $currency_rate = ''; + $tax = 0; + $service_charge = 0; + + foreach ($this->createSupplierTransactionProcessor->getBills() as $key => $row) { + $group->transactions()->sync($row->id, false); + $issuer = $row->issuer; + $receiver = $row->receiver; + $amount += $row->amount; + $original_amount += $row->original_amount; + $currency_id = $row->currency_id; + $original_currency_id = $row->original_currency_id; + $currency_rate = $row->currency_rate; + $tax += $row->tax; + $service_charge += $row->service_charge; + } + + $group->issuer = $issuer; + $group->receiver = $receiver; + $group->reference = $this->generatesTransactionBillNumber->execute('SPO-'); + $group->amount = $amount; + $group->original_amount = $original_amount; + $group->currency_id = $currency_id; + $group->original_currency_id = $original_currency_id; + $group->currency_rate = $currency_rate; + $group->tax = $tax; + $group->service_charge = $service_charge; + + $group->update(); + $pdf = LaravelMpdf::loadView('pages.pdfs.currency_vendor_order', ['transactions' => $this->createSupplierTransactionProcessor->getBills(), 'transferFeeTransactions' => $this->createSupplierTransactionProcessor->getTransferTransactions(), 'supplier' => $supplier]); $object = new DocumentObject( @@ -82,9 +129,10 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic ); /** @var Document $document */ - $document = $this->createsDocument->execute($supplier, $object); + $document = $this->createsDocument->execute($group, $object); $this->createsFile->execute($document, $object); + return $this->response([]); } } diff --git a/app/Classes/Modules/Transactions/ControllersLogic/DeleteGroupLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/DeleteGroupLogic.php new file mode 100644 index 00000000..601a1530 --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/DeleteGroupLogic.php @@ -0,0 +1,73 @@ + 'Delete Group Transaction', + 'message' => 'You have successfully deleted this Group Transaction' + ]; + } + + /** @var UpdatesTransactionStatus */ + private $updatesTransactionStatus; + + /** @var FetchesGroup */ + private $fetchesGroup; + + /** @var DeletesTransaction */ + private $deletesTransaction; + + /** + * DeleteGroupLogic constructor. + * @param updatesTransactionStatus $updatesTransactionStatus + * @param FetchesGroup $fetchesGroup + * @param DeletesTransaction $deletesTransaction + */ + public function __construct(updatesTransactionStatus $updatesTransactionStatus, FetchesGroup $fetchesGroup, DeletesTransaction $deletesTransaction) + { + $this->updatesTransactionStatus = $updatesTransactionStatus; + $this->fetchesGroup = $fetchesGroup; + $this->deletesTransaction = $deletesTransaction; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $group = $this->fetchesGroup->execute(['id' => $request->route('id')]); + + $items = $group->transactions()->get(); + + foreach($items as $item) { + $bill = $item; + $payment = $bill->owner; + $group->transactions()->detach($bill->id); + $this->updatesTransactionStatus->execute($payment, ApprovalStatus::APPROVED); + $this->deletesTransaction->execute($bill); + } + + $group->delete(); + + return $this->resourceResponse(new GroupResource($group)); + } + +} \ No newline at end of file diff --git a/app/Classes/Modules/Transactions/ControllersLogic/DownloadMockUpWhiteFormPdfLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/DownloadMockUpWhiteFormPdfLogic.php index e1875f0a..09dfddb8 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/DownloadMockUpWhiteFormPdfLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/DownloadMockUpWhiteFormPdfLogic.php @@ -6,9 +6,8 @@ namespace App\Classes\Modules\Transactions\ControllersLogic; use App\Classes\Modules\Transactions\Processors\CreateSupplierTransactionProcessor; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use Meneses\LaravelMpdf\Facades\LaravelMpdf; +use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf; use App\Classes\Modules\Companies\Services\FetchesCompany; -use Meneses\LaravelLaravelMpdf\Facades\LaravelLaravelMpdf; class DownloadMockUpWhiteFormPdfLogic { diff --git a/app/Classes/Modules/Transactions/ControllersLogic/FetchCompanyTransactionStatementLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/FetchCompanyTransactionStatementLogic.php new file mode 100644 index 00000000..a078a01d --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/FetchCompanyTransactionStatementLogic.php @@ -0,0 +1,73 @@ + '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); + }) + ->orWhere(function ($query) use ($companyId) { + $query + ->where('type', TransactionType::INVOICE) + ->where('owner_type', Booking::class) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereHas('booking', function ($query) use ($companyId) { + $query->where('company_id', $companyId); + }); + }) + ->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/GenerateCreditNotePdfLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfLogic.php new file mode 100644 index 00000000..949dd243 --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfLogic.php @@ -0,0 +1,56 @@ +fetchesTransaction = $fetchesTransaction; + $this->fetchesCompany = $fetchesCompany; + $this->createSupplierTransactionProcessor = $createSupplierTransactionProcessor; + } + + /** + * @param Request $request + * @return string|\Symfony\Component\HttpFoundation\Response + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function execute(Request $request) + { + $transaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]); + + $booking = $transaction->booking; + + $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]); + + $pdf = LaravelMpdf::loadView('pages.pdfs.credit_note', ['transaction' => $transaction, 'booking' => $booking, 'supplier' => $supplier]); + + return $pdf->stream('CreditNote.pdf'); + } +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/ListGroupsLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/ListGroupsLogic.php new file mode 100644 index 00000000..5cf99bbe --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/ListGroupsLogic.php @@ -0,0 +1,42 @@ +listsGroups = $listsGroups; + } + + /** + * @return array + */ + protected function notification():array { + return [ + 'title' => 'Retrieved Groups', + 'message' => 'You have successfully retrieved a list of groups' + ]; + } + + /** @var ListsGroups */ + private $listsGroups; + + public function logic(Request $request) : JsonResponse + { + $query = $this->listsGroups->execute($this->listsGroups->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(GroupResource::collection($query)); + } + +} 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/ControllersLogic/UpdateGroupLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/UpdateGroupLogic.php new file mode 100644 index 00000000..aa432aea --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/UpdateGroupLogic.php @@ -0,0 +1,185 @@ + 'Update Group Transaction', + 'message' => 'You have successfully updated this Group Transaction' + ]; + } + + /** @var FetchesGroup */ + private $fetchesGroup; + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** @var CalculatesTransactionServiceCharge */ + private $calculatesTransactionServiceCharge; + + /** @var UpdatesTransaction */ + private $updatesTransaction; + + /** @var CalculatesTransactionTransferFee */ + private $calculatesTransactionTransferFee; + + /** @var CreatesDocument */ + private $createsDocument; + + /** @var CreatesFiles */ + private $createsFile; + + /** + * UpdateGroupLogic constructor. + * @param FetchesGroup $fetchesGroup + * @param FetchesCompany $fetchesCompany + * @param CalculatesTransactionServiceCharge $calculatesTransactionServiceCharge + * @param UpdatesTransaction $updatesTransaction + * @param CalculatesTransactionTransferFee $calculatesTransactionTransferFee + * @param CreatesDocument $createsDocument + * @param CreatesFiles $createsFile + */ + public function __construct(FetchesGroup $fetchesGroup, FetchesCompany $fetchesCompany, CalculatesTransactionServiceCharge $calculatesTransactionServiceCharge, UpdatesTransaction $updatesTransaction, CalculatesTransactionTransferFee $calculatesTransactionTransferFee, CreatesDocument $createsDocument, CreatesFiles $createsFile) + { + $this->fetchesGroup = $fetchesGroup; + $this->fetchesCompany = $fetchesCompany; + $this->calculatesTransactionServiceCharge = $calculatesTransactionServiceCharge; + $this->updatesTransaction = $updatesTransaction; + $this->calculatesTransactionTransferFee = $calculatesTransactionTransferFee; + $this->createsDocument = $createsDocument; + $this->createsFile = $createsFile; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $group = $this->fetchesGroup->execute(['id' => $request->route('id')]); + + $transactions = $group->transactions()->get(); + + $rate = $request->input('rate'); + + $supplier = $this->fetchesCompany->execute(['id' => $request->input('supplier_id')]); + +// $group->transactions()->update(['issuer' => $supplier->id, 'currency_rate' => $rate]); +// $group->transactions()->update(['amount' => DB::raw('(original_amount * (1 / currency_rate)) + service_charge + tax')]); + + + foreach($transactions as $transaction) { + + $constant = SegmentConstant::where('reference', SegmentConstants::SERVICE_CHARGE)->where('detail->id', $supplier->id)->first(); + $serviceCharge = $this->calculatesTransactionServiceCharge->execute($transaction->original_amount, $rate, $constant); + + $object = new TransactionObject( + $transaction->bill_no, + TransactionType::BILL, + $supplier->id, + 1, + $supplier->banks()->where('default', true)->first()->id, + PaymentMethodType::CASH, + $transaction->original_amount * (1 / $rate), + $transaction->original_amount, + 1, + $transaction->original_currency_id, + $rate, + 0, + $serviceCharge, + null, + ApprovalStatus::PENDING_VERIFICATION + ); + + $billTransaction = $this->updatesTransaction->execute($transaction, $object); + + $transferTransaction = $transaction->transactions()->where('type', TransactionType::TRANSFER_FEE)->first(); + + $transferFee = $this->calculatesTransactionTransferFee->execute($billTransaction->original_amount, $constant); + + $object = new TransactionObject( + $transferTransaction->bill_no, + TransactionType::TRANSFER_FEE, + $supplier->id, + 1, + $supplier->banks()->where('default', true)->first()->id, + PaymentMethodType::CASH, + $transaction->original_amount, + $transaction->original_amount, + $transaction->original_currency_id, + $transaction->original_currency_id, + 1, + 0, + $transferFee, + null, + ApprovalStatus::PENDING_VERIFICATION + ); + + $this->updatesTransaction->execute($transferTransaction, $object); + } + + $group->issuer = $supplier->id; + $group->amount = $group->transactions()->sum('amount'); + $group->currency_rate = $rate; + $group->tax = $group->transactions()->sum('tax'); + $group->service_charge = $group->transactions()->sum('service_charge'); + + $group->save(); + + $group->documents()->delete(); + + $transferFeeTransactions = $group->transactions()->with([ + 'transactions' => function ($transaction) { + return $transaction->where('type', TransactionType::TRANSFER_FEE); + }])->get()->pluck('transactions')->flatten(); + + $pdf = LaravelMpdf::loadView('pages.pdfs.currency_vendor_order', ['transactions' => $group->transactions, 'transferFeeTransactions' => $transferFeeTransactions, 'supplier' => $supplier]); + + $object = new DocumentObject( + DocumentType::CURRENCY_VENDOR_ORDER, + [chunk_split('data:application/pdf;base64,'.base64_encode($pdf->output()))], + '', + ApprovalStatus::COMPLETED, + 'currency_vendor_order' + ); + + /** @var Document $document */ + $document = $this->createsDocument->execute($group, $object); + $this->createsFile->execute($document, $object); + + return $this->resourceResponse(new GroupResource($group)); + } + +} diff --git a/app/Classes/Modules/Transactions/Processors/CreateCashBackTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateCashBackTransactionProcessor.php new file mode 100644 index 00000000..738cb2b7 --- /dev/null +++ b/app/Classes/Modules/Transactions/Processors/CreateCashBackTransactionProcessor.php @@ -0,0 +1,122 @@ +createsTransaction = $createsTransaction; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->creditWalletProcessor = $creditWalletProcessor; + } + + + /** + * @param Transaction $transaction + * @return Transaction|\Illuminate\Database\Eloquent\Model + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function execute(Transaction $transaction) + { + // leave this disabled until ready to launch to production + return; + // ((MYR) * (cash back %)) * (1/conversion rate) + $current_total_cash_back = Transaction:: + where('type', TransactionType::CASH_BACK) + ->whereMonth('created_at', Carbon::now()->month)->sum('amount'); + + if ( + $current_total_cash_back < CashBack::MAX && + $transaction->owner()->first()->transactions()->where('type', TransactionType::PURCHASE_ORDER)->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->count() > 0 + ) { + + $cash_back_segemnt = CashBack::SEGMENT; + + foreach ($cash_back_segemnt as $key => $row) { + if ($row['max_value'] > $transaction->amount && $row['min_value'] <= $transaction->amount) { + + $method = $this->getRandomWeightedElement($row['weight']); + + $total = $transaction->amount * $row['percent'][$method]; + + $billNumber = $this->generatesTransactionBillNumber->execute('CBACK-'); + + $object = new TransactionObject( + $billNumber, + TransactionType::CASH_BACK, + $transaction->issuer, + 1, + 1, + PaymentMethodType::WALLET, + $total, + $total, + $transaction->currency_id, + $transaction->currency_id, + 1, + 0, + 0, + null, + ApprovalStatus::APPROVED, + null, + 'cash back ' . $transaction->bill_no + ); + + $cash_back_transaction = $this->createsTransaction->execute($transaction, $object); + + $company = $transaction->owner()->first()->company; + $credit = $this->creditWalletProcessor->execute($company, $transaction->type, $cash_back_transaction->amount, 'cash back ' . $transaction->bill_no); + } + } + } + return $transaction; + } + + public function getRandomWeightedElement(array $weightedValues) { + $rand = mt_rand(1, (int) array_sum($weightedValues)); + foreach ($weightedValues as $key => $value) { + $rand -= $value; + if ($rand <= 0) { + return $key; + } + } + } + +} \ No newline at end of file diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php new file mode 100644 index 00000000..6215a1e1 --- /dev/null +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php @@ -0,0 +1,77 @@ +createsDocument = $createsDocument; + $this->createsFile = $createsFile; + } + + /** + * @param $transaction + * @param $purchaseOrder + * @param $supplier + * @param $document_type + * @return void + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + 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, 'voucher_redemption' => $voucherRedemption]); + + if($purchaseOrder->booking->service_id === 4) { + $purchaseOrderDocuments = $purchaseOrder->booking->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->get(); + + + $oMerger = PDFMerger::init(); + $order_pdf->save(storage_path('app/documents/temp.pdf')); + + $oMerger->addPDF(storage_path('app/documents/temp.pdf'), 'all'); + foreach ($purchaseOrderDocuments as $document){ + $oMerger->addPDF(storage_path('app/documents/'.$document->files()->first()->file->file_info->original->file), 'all'); + } + + $oMerger->merge(); + + $order_pdf = $oMerger; + } + + $document_object = new DocumentObject( + $document_type, + [chunk_split('data:application/pdf;base64,' . base64_encode($order_pdf->output()))], + '', + ApprovalStatus::COMPLETED, + $lowercaseDocumentType . 's' + ); + + /** @var Document $document */ + $document = $this->createsDocument->execute($purchaseOrder->booking, $document_object); + $this->createsFile->execute($document, $document_object); + + } +} diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php index 5a29a1a2..7f105dc8 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php @@ -2,6 +2,7 @@ namespace App\Classes\Modules\Transactions\Processors; +use App\Classes\Exceptions\MalformedRequestException; use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount; use App\Classes\Modules\Bookings\Services\CalculatesBookingTransferredAmount; use App\Classes\Modules\ServiceTypes\Services\FetchesServiceConfigurations; @@ -11,25 +12,17 @@ use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber; use App\Classes\Modules\Bookings\Services\CalculatesBookingPaidAmount; use App\Classes\Modules\Bookings\Services\CalculatesBookingCurrencyAverageRate; use App\Classes\Modules\Companies\Services\FetchesCompany; -use App\Classes\Modules\Documents\Services\CreatesDocument; -use App\Classes\Modules\Documents\Services\CreatesFiles; use App\Classes\Modules\Bookings\Services\UpdatesBookingStatus; - use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject; -use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\SegmentConstants; use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\ValueObjects\Constants\DocumentType; use App\Models\Booking; -use App\Models\Document; use App\Models\SegmentConstant; -use Meneses\LaravelMpdf\Facades\LaravelMpdf; class CreateInvoiceTransactionProcessor { - /** @var ListsTransactions */ - private $listsTransactions; /** @var CreatesTransaction */ private $createsTransaction; @@ -46,24 +39,19 @@ class CreateInvoiceTransactionProcessor /** @var CalculatesBookingTransferredAmount */ private $calculatesBookingTransferredAmount; - /** @var FetchesServiceConfigurations */ - private $fetchesServiceConfigurations; - /** @var CalculatesBookingCurrencyAverageRate */ private $calculatesBookingCurrencyAverageRate; /** @var FetchesCompany */ private $fetchesCompany; - /** @var CreatesDocument */ - private $createsDocument; - - /** @var CreatesFiles */ - private $createsFile; - /** @var UpdatesBookingStatus */ private $updatesBookingStatus; + /** @var CreateInvoiceDocumentProcessor */ + private $invoiceDocumentProcessor; + + /** * CreateInvoiceTransactionProcessor constructor. * @param ListsTransactions $listsTransactions @@ -75,33 +63,30 @@ class CreateInvoiceTransactionProcessor * @param FetchesServiceConfigurations $fetchesServiceConfigurations * @param CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate * @param FetchesCompany $fetchesCompany - * @param CreatesDocument $createsDocument - * @param CreatesFiles $createsFile * @param UpdatesBookingStatus $updatesBookingStatus + * @param CreateInvoiceDocumentProcessor $invoiceDocumentProcessor */ - public function __construct(ListsTransactions $listsTransactions, CreatesTransaction $createsTransaction, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CalculatesBookingPaidAmount $calculatesBookingPaidAmount, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingTransferredAmount $calculatesBookingTransferredAmount, FetchesServiceConfigurations $fetchesServiceConfigurations, CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate, FetchesCompany $fetchesCompany, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesBookingStatus $updatesBookingStatus) + public function __construct(ListsTransactions $listsTransactions, CreatesTransaction $createsTransaction, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CalculatesBookingPaidAmount $calculatesBookingPaidAmount, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingTransferredAmount $calculatesBookingTransferredAmount, FetchesServiceConfigurations $fetchesServiceConfigurations, CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate, FetchesCompany $fetchesCompany, UpdatesBookingStatus $updatesBookingStatus, CreateInvoiceDocumentProcessor $invoiceDocumentProcessor) { - $this->listsTransactions = $listsTransactions; $this->createsTransaction = $createsTransaction; $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; $this->calculatesBookingPaidAmount = $calculatesBookingPaidAmount; $this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount; $this->calculatesBookingTransferredAmount = $calculatesBookingTransferredAmount; - $this->fetchesServiceConfigurations = $fetchesServiceConfigurations; $this->calculatesBookingCurrencyAverageRate = $calculatesBookingCurrencyAverageRate; $this->fetchesCompany = $fetchesCompany; - $this->createsDocument = $createsDocument; - $this->createsFile = $createsFile; $this->updatesBookingStatus = $updatesBookingStatus; + $this->invoiceDocumentProcessor = $invoiceDocumentProcessor; } + /** * @param Booking $booking * @return void - * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws MalformedRequestException */ - public function execute(Booking $booking) + public function execute(Booking $booking) { if ($booking->status === ApprovalStatus::COMPLETED) { @@ -116,24 +101,28 @@ class CreateInvoiceTransactionProcessor return; } // confirm that all payments has been transferred - if($this->calculatesBookingTransferredAmount->execute($booking) !== $this->calculatesBookingPaidAmount->execute($booking)){ + if ($this->calculatesBookingTransferredAmount->execute($booking) !== $this->calculatesBookingPaidAmount->execute($booking)) { return; } - $po_order_transaction = $booking->transactions() + $purchaseOrder = $booking->transactions() ->where('type', TransactionType::PURCHASE_ORDER) ->complete() ->first(); $constants = SegmentConstant::where('reference', SegmentConstants::SERVICE_TYPE)->where('detail->id', $booking->service->id)->first(); - if($constants->detail->is_billable && !$po_order_transaction) { + if ($constants->detail->is_billable && !$purchaseOrder) { 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-'); @@ -166,46 +155,20 @@ class CreateInvoiceTransactionProcessor null, ApprovalStatus::APPROVED ); - $invoice_transaction = $this->createsTransaction->execute($po_order_transaction->booking, $transaction_object); + $invoice_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object); + + $voucherRedemption = $transaction->voucherRedemption; $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]); - $purchase_order_pdf = LaravelMpdf::loadView('pages.pdfs.purchase_order', ['invoice_transaction' => $invoice_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]); - $document_object = new DocumentObject( - DocumentType::PURCHASE_ORDER, - [chunk_split('data:application/pdf;base64,'.base64_encode($purchase_order_pdf->output()))], - '', - ApprovalStatus::COMPLETED, - 'purchase_orders' - ); - /** @var Document $document */ - $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object); - $this->createsFile->execute($document, $document_object); + // purchase order + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER, $voucherRedemption); - $deliver_order_pdf = LaravelMpdf::loadView('pages.pdfs.deliver_order', ['invoice_transaction' => $invoice_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]); - $document_object = new DocumentObject( - DocumentType::DELIVER_ORDER, - [chunk_split('data:application/pdf;base64,'.base64_encode($deliver_order_pdf->output()))], - '', - ApprovalStatus::COMPLETED, - 'delivery_orders' - ); + // deliver order + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::DELIVER_ORDER, $voucherRedemption); - /** @var Document $document */ - $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object); - $this->createsFile->execute($document, $document_object); - - - $invoice_pdf = LaravelMpdf::loadView('pages.pdfs.invoice', ['invoice_transaction' => $invoice_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]); - $document_object = new DocumentObject( - DocumentType::INVOICE, - [chunk_split('data:application/pdf;base64,'.base64_encode($invoice_pdf->output()))], - '', - ApprovalStatus::COMPLETED, - 'invoices' - ); - $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object); - $this->createsFile->execute($document, $document_object); + // invoice + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::INVOICE, $voucherRedemption); $billNumber = $this->generatesTransactionBillNumber->execute('SPDO-'); @@ -231,19 +194,16 @@ class CreateInvoiceTransactionProcessor null, ApprovalStatus::APPROVED ); - $supplier_deliver_order_transaction = $this->createsTransaction->execute($po_order_transaction->booking, $transaction_object); + $supplier_deliver_order_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object); - $supplier_order_pdf = LaravelMpdf::loadView('pages.pdfs.supplier_deliver_order', ['supplier_deliver_order_transaction' => $supplier_deliver_order_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]); - $document_object = new DocumentObject( - DocumentType::SUPPLIER_DELIVER_ORDER, - [chunk_split('data:application/pdf;base64,'.base64_encode($supplier_order_pdf->output()))], - '', - ApprovalStatus::COMPLETED, - 'supplier_delivery_orders' - ); - $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object); - $this->createsFile->execute($document, $document_object); + // supply deliver order + $this->invoiceDocumentProcessor->execute($supplier_deliver_order_transaction, $purchaseOrder, $supplier, DocumentType::SUPPLIER_DELIVER_ORDER, null); $this->updatesBookingStatus->execute($booking, ApprovalStatus::COMPLETED); + + // update perfex crm + // if(config('perfexcrm.is_enabled') == 'true'){ + // CreatePerfexCRMInvoice::dispatch($invoice_transaction, $purchaseOrder, $supplier); + // } } } diff --git a/app/Classes/Modules/Transactions/Processors/CreateProformaInvoiceTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateProformaInvoiceTransactionProcessor.php index 3e7e8137..8dab2d24 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateProformaInvoiceTransactionProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateProformaInvoiceTransactionProcessor.php @@ -24,7 +24,7 @@ use App\Classes\ValueObjects\Constants\DocumentType; use App\Models\Booking; use App\Models\Document; use Carbon\Carbon; -use Meneses\LaravelMpdf\Facades\LaravelMpdf; +use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf; class CreateProformaInvoiceTransactionProcessor { @@ -100,7 +100,7 @@ class CreateProformaInvoiceTransactionProcessor * @return void * @throws \App\Classes\Exceptions\MalformedRequestException */ - public function execute(Booking $booking) + public function execute(Booking $booking) { $po_order_transaction = $booking->transactions() @@ -129,14 +129,26 @@ class CreateProformaInvoiceTransactionProcessor $billNumber = $this->generatesTransactionBillNumber->execute('PROFORMA-'); - $payable_amount = $booking->transactions()->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::SUSPENDED])->payments()->sum('amount'); + $payable_amount = $booking->transactions()->payments()->where(function($query){ + return $query->where(function($query){ + return $query->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString()); + })->orWhere(function($query){ + return $query->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + }); + })->sum('amount'); $booking_amount = $booking->fix_amount; $transaction = $booking->transactions() ->where('type', TransactionType::PAYMENT) ->first(); - $booking_currency_average_rate = $booking_amount / $booking->transactions()->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::SUSPENDED])->payments()->selectRaw('sum(amount - service_charge - tax) as sub_total')->get()->sum('sub_total'); + $booking_currency_average_rate = $booking_amount / $booking->transactions()->payments()->where(function($query){ + return $query->where(function($query){ + return $query->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString()); + })->orWhere(function($query){ + return $query->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + }); + })->selectRaw('sum(amount - service_charge - tax) as sub_total')->get()->sum('sub_total'); $total_service_charge = $booking->transactions() ->where('type', TransactionType::PAYMENT) diff --git a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php index 75516fdf..9b944a5b 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php @@ -90,14 +90,15 @@ class CreateSupplierTransactionProcessor $object = new TransactionObject($billNumber, TransactionType::BILL, $supplier->id, 1, $supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH, $payment->original_amount * (1 / $rate), $payment->original_amount, 1, $payment->original_currency_id, - $rate, 0, $serviceCharge, null, ApprovalStatus::PENDING_VERIFICATION); + $rate, 0, $serviceCharge, null, ApprovalStatus::PENDING_SUBMISSION); /** @var Transaction $billTransaction */ $billTransaction = $this->createsTransaction->execute($payment, $object); + $this->updatesTransactionStatus->execute($billTransaction, ApprovalStatus::PENDING_VERIFICATION); $this->pushBill($billTransaction); $transferFeeNumber = $this->generatesTransactionBillNumber->execute('TRFR-'); - $transferFee = $this->calculatesTransactionTransferFee->execute($payment->original_amount, $constant); + $transferFee = $this->calculatesTransactionTransferFee->execute($billTransaction->original_amount, $constant); $object = new TransactionObject($transferFeeNumber, TransactionType::TRANSFER_FEE, 1, $supplier->id, $supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH, $payment->original_amount, $payment->original_amount, $payment->original_currency_id, $payment->original_currency_id, @@ -140,4 +141,4 @@ class CreateSupplierTransactionProcessor $this->transferFee->push($transferFee); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsPurchaseOrder.php b/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsPurchaseOrder.php new file mode 100644 index 00000000..91267ff2 --- /dev/null +++ b/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsPurchaseOrder.php @@ -0,0 +1,89 @@ +listsGroups = $listsGroups; + $this->fetchesCompany = $fetchesCompany; + $this->createsDocument = $createsDocument; + $this->createsFile = $createsFile; + } + + public function execute(){ + $groups = Group::whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->whereDoesntHave('transactions', function ($query){ + $query->whereHasMorph('owner', [Transaction::class], function($query){ + return $query->whereHas('booking', function($query){ + return $query->whereDoesntHave('transactions', function($query){ + return $query->where('type', TransactionType::PURCHASE_ORDER)->where('status', '=', ApprovalStatus::APPROVED); + }); + }); + }); + })->get(); + + + foreach ($groups as $group) { +// if ($transaction->owner()->owner()->transactions()->where('type', TransactionType::PURCHASE_ORDER)->where('status', '!=', ApprovalStatus::APPROVED)->exists()) { +// throw new MalformedRequestException('You can\'t generate bulk purchased order if there in uncomplete transactions'); +// } + $supplier = $this->fetchesCompany->execute(['id' => $group->issuer]); + + $document_type = DocumentType::BULK_PURCHASE_ORDER; + + $lowercaseDocumentType = strtolower($document_type); + + $order_pdf = LaravelMpdf::loadView('pages.pdfs.bulk_purchase_order', ['group' => $group, 'supplier' => $supplier]); + $document_object = new DocumentObject( + $document_type, + [chunk_split('data:application/pdf;base64,' . base64_encode($order_pdf->output()))], + '', + ApprovalStatus::COMPLETED, + $lowercaseDocumentType . 's' + ); + + /** @var Document $document */ + $document = $this->createsDocument->execute($group, $document_object); + $this->createsFile->execute($document, $document_object); + + $group->status = ApprovalStatus::COMPLETED; + $group->save(); + + } + } +} diff --git a/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsWhiteForm.php b/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsWhiteForm.php new file mode 100644 index 00000000..2b66b478 --- /dev/null +++ b/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsWhiteForm.php @@ -0,0 +1,74 @@ +listsGroups = $listsGroups; + $this->fetchesCompany = $fetchesCompany; + $this->createsDocument = $createsDocument; + $this->createsFile = $createsFile; + } + + + public function execute(){ + $groups = Group::where('status', ApprovalStatus::PENDING_SUBMISSION)->get(); + + + foreach ($groups as $group) { + $pdf = LaravelMpdf::loadView('pages.pdfs.currency_vendor_order', ['transactions' => $group->transactions, 'transferFeeTransactions' => $group->transferFees, 'supplier' => $group->issuerCompany]); + + $object = new DocumentObject( + DocumentType::CURRENCY_VENDOR_ORDER, + [chunk_split('data:application/pdf;base64,'.base64_encode($pdf->output()))], + '', + ApprovalStatus::COMPLETED, + 'currency_vendor_order' + ); + + /** @var Document $document */ + $document = $this->createsDocument->execute($group, $object); + $this->createsFile->execute($document, $object); + + $group->status = ApprovalStatus::APPROVED; + $group->save(); + + } + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Transactions/Processors/UpdateWalletTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/UpdateWalletTransactionProcessor.php index 7c7277f8..c1d36edb 100644 --- a/app/Classes/Modules/Transactions/Processors/UpdateWalletTransactionProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/UpdateWalletTransactionProcessor.php @@ -27,6 +27,9 @@ class UpdateWalletTransactionProcessor /** @var UpdatesTransactionStatus */ private $updatesTransactionStatus; + /** @var UpdatesWallet */ + private $updatesWallet; + public function __construct(UpdatesTransactionStatus $updatesTransactionStatus, UpdatesWallet $updatesWallet, FetchesTransaction $fetchesTransaction) { $this->fetchesTransaction = $fetchesTransaction; diff --git a/app/Classes/Modules/Transactions/Services/CalculatesTransactionServiceCharge.php b/app/Classes/Modules/Transactions/Services/CalculatesTransactionServiceCharge.php index e04edee2..695b465e 100644 --- a/app/Classes/Modules/Transactions/Services/CalculatesTransactionServiceCharge.php +++ b/app/Classes/Modules/Transactions/Services/CalculatesTransactionServiceCharge.php @@ -25,15 +25,18 @@ class CalculatesTransactionServiceCharge * @param SegmentConstant|null $service_charge * @return float */ - public function execute(float $amount, float $rate, ?SegmentConstant $service_charge) { + public function execute(float $amount, float $rate, ?SegmentConstant $service_charge) + { - if(!$service_charge) { + if (!$service_charge) { return 0; } - $transfer_fee = $this->calculatesTransactionTransferFee->execute($amount, $service_charge); - return $service_charge->detail->amount->type === 'percentage' ? (($amount + $transfer_fee) * ( (float) $service_charge->detail->amount->value /100) * (1/$rate)) : (float) $service_charge->detail->amount->value; - + $transfer_fee = $this->calculatesTransactionTransferFee->execute(($amount * (1 / $rate)), $service_charge); + if (isset($service_charge->detail->amount)) { + return $service_charge->detail->amount->type === 'percentage' ? (($amount + $transfer_fee) * ((float) $service_charge->detail->amount->value / 100) * (1 / $rate)) : (float) $service_charge->detail->amount->value; + } else { + return 0; + } } - -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Transactions/Services/CalculatesTransactionTransferFee.php b/app/Classes/Modules/Transactions/Services/CalculatesTransactionTransferFee.php index 5a02a394..0bcaba28 100644 --- a/app/Classes/Modules/Transactions/Services/CalculatesTransactionTransferFee.php +++ b/app/Classes/Modules/Transactions/Services/CalculatesTransactionTransferFee.php @@ -12,13 +12,17 @@ class CalculatesTransactionTransferFee * @param SegmentConstant|null $service_charge * @return float */ - public function execute(float $amount, ?SegmentConstant $service_charge) { + public function execute(float $amount, ?SegmentConstant $service_charge) + { - if(!$service_charge) { + if (!$service_charge) { return 0; } - return $service_charge->detail->transferFee->type === 'percentage' ? $amount * ((float) $service_charge->detail->transferFee->value /100) : (float) $service_charge->detail->transferFee->value; + if (isset($service_charge->detail->transferFee)) { + return $service_charge->detail->transferFee->type === 'percentage' ? $amount * ((float) $service_charge->detail->transferFee->value / 100) : (float) $service_charge->detail->transferFee->value; + } else { + return 0; + } } - -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Transactions/Services/ChecksIfGroupTransactionBillNumberExists.php b/app/Classes/Modules/Transactions/Services/ChecksIfGroupTransactionBillNumberExists.php new file mode 100644 index 00000000..d3be2121 --- /dev/null +++ b/app/Classes/Modules/Transactions/Services/ChecksIfGroupTransactionBillNumberExists.php @@ -0,0 +1,22 @@ +repository = $repository; + } + + public function execute(string $bill_no): bool { + return $this->repository->where('reference', $bill_no)->exists(); + } + +} \ No newline at end of file diff --git a/app/Classes/Modules/Transactions/Services/FetchesGroup.php b/app/Classes/Modules/Transactions/Services/FetchesGroup.php new file mode 100644 index 00000000..6533664b --- /dev/null +++ b/app/Classes/Modules/Transactions/Services/FetchesGroup.php @@ -0,0 +1,31 @@ +repository = $repository; + } + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Transactions/Services/GeneratesGroupTransactionBillNumber.php b/app/Classes/Modules/Transactions/Services/GeneratesGroupTransactionBillNumber.php new file mode 100644 index 00000000..a8aab459 --- /dev/null +++ b/app/Classes/Modules/Transactions/Services/GeneratesGroupTransactionBillNumber.php @@ -0,0 +1,39 @@ +checksIfGroupTransactionBillNumberExists = $checksIfGroupTransactionBillNumberExists; + } + + /** + * @param string $prefix + * @param Carbon|null $date + * @return string + */ + public function execute(string $prefix, ?Carbon $date = null): string { + if(!$date){ + $date = carbon::now(); + } + + $billNumber = $prefix.$date->format('Y').$date->format('m').'-'.mt_rand(10000, 99999); + + return !$this->checksIfGroupTransactionBillNumberExists->execute($billNumber) ? $billNumber : self::execute($prefix); + + } + +} \ No newline at end of file diff --git a/app/Classes/Modules/Transactions/Services/GeneratesTransactionBillNumber.php b/app/Classes/Modules/Transactions/Services/GeneratesTransactionBillNumber.php index 7f92ab60..943da696 100644 --- a/app/Classes/Modules/Transactions/Services/GeneratesTransactionBillNumber.php +++ b/app/Classes/Modules/Transactions/Services/GeneratesTransactionBillNumber.php @@ -3,6 +3,7 @@ namespace App\Classes\Modules\Transactions\Services; +use App\Classes\Exceptions\InternalServerErrorException; use Carbon\Carbon; class GeneratesTransactionBillNumber @@ -23,15 +24,25 @@ class GeneratesTransactionBillNumber /** * @param string $prefix + * @param Carbon|null $date * @return string */ - public function execute(string $prefix): string { - $date = carbon::now(); + public function execute(string $prefix, ?Carbon $date = null): string { + if (!$date) { + $date = Carbon::now(); + } - $billNumber = $prefix.$date->format('Y').$date->format('m').'-'.mt_rand(10000, 99999); - - return !$this->checksIfTransactionBillNumberExists->execute($billNumber) ? $billNumber : self::execute($prefix); + $attempt = 0; + while ($attempt < 10) { // Retry up to 10 times + $billNumber = $prefix . $date->format('Y') . $date->format('m') . '-' . microtime(true); + if (!$this->checksIfTransactionBillNumberExists->execute($billNumber)) { + return $billNumber; + } + $attempt++; + } + throw new InternalServerErrorException("Unable to generate unique bill number after {$attempt} attempts."); } -} \ No newline at end of file + +} diff --git a/app/Classes/Modules/Transactions/Services/ListsGroups.php b/app/Classes/Modules/Transactions/Services/ListsGroups.php new file mode 100644 index 00000000..f9126739 --- /dev/null +++ b/app/Classes/Modules/Transactions/Services/ListsGroups.php @@ -0,0 +1,31 @@ +repository = $repository; + } + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Transactions/Services/UpdatesTransaction.php b/app/Classes/Modules/Transactions/Services/UpdatesTransaction.php index aba7b07a..d1c5065f 100644 --- a/app/Classes/Modules/Transactions/Services/UpdatesTransaction.php +++ b/app/Classes/Modules/Transactions/Services/UpdatesTransaction.php @@ -2,19 +2,21 @@ namespace App\Classes\Modules\Transactions\Services; +use App\Classes\Exceptions\MalformedRequestException; use App\Classes\General\Eloquent\AbstractUpdateRecord; use App\Classes\General\Eloquent\AbstractUpdateRelationshipRecord; use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject; use App\Models\Booking; use App\Models\Transaction; +use Illuminate\Database\Eloquent\Model; class UpdatesTransaction extends AbstractUpdateRecord { /** * @param Transaction $transaction * @param TransactionObject $object - * @return \Illuminate\Database\Eloquent\Model - * @throws \App\Classes\Exceptions\MalformedRequestException + * @return Model + * @throws MalformedRequestException */ public function execute(Transaction $transaction, TransactionObject $object) { $transaction->recipient_bank_account_id = $object->getRecipientBankAccountId(); @@ -30,4 +32,4 @@ class UpdatesTransaction extends AbstractUpdateRecord return $this->handler($transaction); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php b/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php index 769f2dc0..d92e3e9a 100644 --- a/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php +++ b/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php @@ -3,10 +3,31 @@ 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\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, TransactionToVoucherifyProcessor $transactionToVoucherifyProcessor) + { + $this->transactionToPerfexCRMProcessor = $transactionToPerfexCRMProcessor; + $this->transactionToVoucherifyProcessor = $transactionToVoucherifyProcessor; + } + /** * @param Transaction $model @@ -16,7 +37,17 @@ class UpdatesTransactionStatus extends AbstractUpdateRecord */ public function execute(Transaction $model, int $status) { + $transaction = clone($model); $model->status = $status; - return $this->handler($model); + $result = $this->handler($model); + 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; } -} \ No newline at end of file +} 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..c483d45e --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyOrderObject.php @@ -0,0 +1,95 @@ +employee = $employee; + $this->companyId = $companyId; + $this->transactionId = $transactionId; + $this->amount = $amount; + $this->isNoVoucher = $isNoVoucher; + $this->isTopUpWallet = $isTopUpWallet; + } + + /** + * @return int + */ + public function getCompanyId(): int + { + return $this->companyId; + } + + /** + * @return int + */ + public function getTransactionId(): int + { + return $this->transactionId; + } + + /** + * @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..325df2f4 --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/RedeemVoucherifyVoucherObject.php @@ -0,0 +1,83 @@ +companyId = $companyId; + $this->transactionId = $transactionId; + $this->promoCode = $promoCode; + $this->amount = $amount; + $this->employee = $employee; + } + + /** + * @return int + */ + public function getCompanyId(): int + { + return $this->companyId; + } + + + /** + * @return int + */ + public function getTransactionId(): int + { + return $this->transactionId; + } + + + /** + * @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..323b2eaf --- /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, $transaction->id, $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, $transaction->id, $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..90e5d682 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyOrder.php @@ -0,0 +1,62 @@ +voucherifyClient = createVoucherifyClient(); + } + + + /** + * @param CreateVoucherifyOrderObject $obj + * @return null|object + * @throws \Voucherify\ClientException + */ + public function execute(CreateVoucherifyOrderObject $obj) + { + try { + $orderObj = [ + "source_id" => $obj->getTransactionId(), + "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..7c44889b --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/Voucherify/RedeemsVoucherifyVoucher.php @@ -0,0 +1,51 @@ +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" => [ + "source_id" => $redeemVoucherifyVoucherObject->getTransactionId(), + "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 9bbe6073..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; } /** @@ -102,12 +107,14 @@ class TopUpWalletLogic extends AbstractControllerLogic throw new MalformedRequestException('Top up credit value must be greater than zero.'); } - $billPlzBill = $this->createsBillplzBill->execute($user->name, $user->email, 'This payment is credit topup for company ref. ' . $company->reference, $amount, $billNumber, $request->input('bank_code'), true); + $billPlzBill = $this->createsBillplzBill->execute($company->name, $user->email, 'This payment is credit topup for company ref. ' . $company->reference, $amount, $billNumber, $request->input('bank_code'), true); $transaction_object = new TransactionObject($billNumber, TransactionType::TOP_UP, 1, $company->id, 1, PaymentMethodType::PAYMENT_GATEWAY, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, [], $billPlzBill->id); $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/Modules/Wallets/Processors/CreditWalletProcessor.php b/app/Classes/Modules/Wallets/Processors/CreditWalletProcessor.php index d86a4465..65f3b9b6 100644 --- a/app/Classes/Modules/Wallets/Processors/CreditWalletProcessor.php +++ b/app/Classes/Modules/Wallets/Processors/CreditWalletProcessor.php @@ -78,7 +78,7 @@ class CreditWalletProcessor $transaction_object = new TransactionObject($billNumber, $transactionType === 2 ? TransactionType::DEBIT_NOTE : TransactionType::CREDIT_NOTE, 1, $wallet->owner->id, 1, PaymentMethodType::CASH, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], $reference); $transaction = $this->createsTransaction->execute($wallet, $transaction_object); - + $updateWalletAmount = $transactionType === 2 ? ($wallet->amount - $transaction->amount) : ($wallet->amount + $transaction->amount); $walletObject = new WalletObject($wallet->owner->id, $wallet->currency_id, $wallet->code, $updateWalletAmount); diff --git a/app/Classes/Modules/Wallets/Services/RecalculatesWalletBalance.php b/app/Classes/Modules/Wallets/Services/RecalculatesWalletBalance.php new file mode 100644 index 00000000..651bee4a --- /dev/null +++ b/app/Classes/Modules/Wallets/Services/RecalculatesWalletBalance.php @@ -0,0 +1,48 @@ +updatesWalletBalance = $updatesWalletBalance; + } + /** + * @param Wallet $model + * @param $amount + * @return \Illuminate\Database\Eloquent\Model + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function execute(Wallet $wallet) + { + + $i = 0; + $topups = 0; + $credit = 0; + $payments = 0; + $debit = 0; + + foreach ($wallet->transactions as $transaction) { + if (!in_array((int) $transaction->status, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])) continue; + if ((int) $transaction->type === TransactionType::TOP_UP) { + $topups += (float) $transaction->amount; + } + if ((int) $transaction->type === TransactionType::CREDIT_NOTE) $credit += (float) $transaction->amount; + if ((int) $transaction->type === TransactionType::PAYMENT) $payments += (float) $transaction->amount; + if ((int) $transaction->type === TransactionType::DEBIT_NOTE) $debit += (float) $transaction->amount; + } + $auditBalance = ($topups + $credit) - ($payments + $debit); + + return $auditBalance; + } +} diff --git a/app/Classes/Modules/Wallets/Services/UpdatesWalletBalance.php b/app/Classes/Modules/Wallets/Services/UpdatesWalletBalance.php index a724cf24..4812660a 100644 --- a/app/Classes/Modules/Wallets/Services/UpdatesWalletBalance.php +++ b/app/Classes/Modules/Wallets/Services/UpdatesWalletBalance.php @@ -3,10 +3,9 @@ namespace App\Classes\Modules\Wallets\Services; use App\Classes\General\Eloquent\AbstractUpdateRecord; -use App\Classes\General\Eloquent\AbstractUpdateRelationshipRecord; -use App\Classes\Modules\Wallets\DataTransferObjects\WalletObject; +use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Wallet; -use App\Models\Company; class UpdatesWalletBalance extends AbstractUpdateRecord { @@ -16,10 +15,24 @@ class UpdatesWalletBalance extends AbstractUpdateRecord * @return \Illuminate\Database\Eloquent\Model * @throws \App\Classes\Exceptions\MalformedRequestException */ - public function execute(Wallet $model, $amount) { + public function execute(Wallet $model, $amount) + { + $i = 0; + $topups = 0; + $credit = 0; + $payments = 0; + $debit = 0; - $model->amount = $model->amount + $amount; + foreach ($model->transactions as $transaction) { + if (!in_array((int) $transaction->status, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])) continue; + if ((int) $transaction->type === TransactionType::TOP_UP) $topups += (float) $transaction->amount; + if ((int) $transaction->type === TransactionType::CREDIT_NOTE) $credit += (float) $transaction->amount; + if ((int) $transaction->type === TransactionType::PAYMENT) $payments += (float) $transaction->amount; + if ((int) $transaction->type === TransactionType::DEBIT_NOTE) $debit += (float) $transaction->amount; + } + $auditBalance = ($topups + $credit) - ($payments + $debit); + + $model->amount = $auditBalance; return $this->handler($model); - } } 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/ApprovalStatus.php b/app/Classes/ValueObjects/Constants/ApprovalStatus.php index 154db2ca..ba7660d4 100644 --- a/app/Classes/ValueObjects/Constants/ApprovalStatus.php +++ b/app/Classes/ValueObjects/Constants/ApprovalStatus.php @@ -19,4 +19,15 @@ final class ApprovalStatus { public const EXPIRED = 6; public const REFUNDED = 7; + + public const APPROVAL_STATUS_ID = [ + self::PENDING_SUBMISSION => "Pending Submission", + self::PENDING_VERIFICATION => "Pending Verification", + self::APPROVED => "Approved", + self::COMPLETED => "Completed", + self::REJECTED => "Rejected", + self::SUSPENDED => "Suspended", + self::EXPIRED => "Expired", + self::REFUNDED => "Refunded", + ]; } diff --git a/app/Classes/ValueObjects/Constants/BusinessType.php b/app/Classes/ValueObjects/Constants/BusinessType.php index 831568e9..69ea3202 100644 --- a/app/Classes/ValueObjects/Constants/BusinessType.php +++ b/app/Classes/ValueObjects/Constants/BusinessType.php @@ -12,4 +12,11 @@ final class BusinessType { public const TRANSFER_AGENT = 4; + public const BUSINESS_TYPE_LIST = [ + self::FREIGHT_FORWARDER => 'Freight Forwarder', + self::IMPORTER => 'Importer', + self::CURRENCY_VENDOR => 'Currency Vendor', + self::TRANSFER_AGENT => 'Transfer Agent', + ]; + } diff --git a/app/Classes/ValueObjects/Constants/CashBack.php b/app/Classes/ValueObjects/Constants/CashBack.php new file mode 100644 index 00000000..0fdc3df9 --- /dev/null +++ b/app/Classes/ValueObjects/Constants/CashBack.php @@ -0,0 +1,55 @@ + [ + 'min_value' => 0, + 'max_value' => 2000, + 'weight' => [ + '0' => 80, + '1' => 18, + '2' => 2, + ], + 'percent' => [ + '0' => 0.002, + '1' => 0.005, + '2' => 0.02, + ] + ], + '1' => [ + 'min_value' => 2001, + 'max_value' => 10000, + 'weight' => [ + '0' => 85, + '1' => 10, + '2' => 5, + ], + 'percent' => [ + '0' => 0.002, + '1' => 0.005, + '2' => 0.02, + ] + ], + '2' => [ + 'min_value' => 10001, + 'max_value' => 100000, + 'weight' => [ + '0' => 70, + '1' => 20, + '2' => 10, + ], + 'percent' => [ + '0' => 0.002, + '1' => 0.005, + '2' => 0.02, + ] + ], + ]; +} diff --git a/app/Classes/ValueObjects/Constants/CompanyType.php b/app/Classes/ValueObjects/Constants/CompanyType.php index 61918925..f16e51c5 100644 --- a/app/Classes/ValueObjects/Constants/CompanyType.php +++ b/app/Classes/ValueObjects/Constants/CompanyType.php @@ -8,4 +8,14 @@ final class CompanyType { public const COMPANY_BUSINESS = 1; + public const COMPANY_TYPE_ID = [ + self::PERSONAL_BUSINESS => 'PERSONAL_BUSINESS', + self::COMPANY_BUSINESS => 'COMPANY_BUSINESS', + ]; + + public const COMPANY_TYPE_LIST = [ + self::PERSONAL_BUSINESS => 'Personal Business', + self::COMPANY_BUSINESS => 'Company Business', + ]; + } diff --git a/app/Classes/ValueObjects/Constants/DocumentType.php b/app/Classes/ValueObjects/Constants/DocumentType.php index 04edf25c..23f9a94a 100644 --- a/app/Classes/ValueObjects/Constants/DocumentType.php +++ b/app/Classes/ValueObjects/Constants/DocumentType.php @@ -17,9 +17,12 @@ final class DocumentType { public const WALLET_TOP_UP_PAYMENT_PROOF = 'WALLET_TOP_UP_PAYMENT_PROOF'; public const WALLET_REFUND_PAYMENT_PROOF = 'WALLET_REFUND_PAYMENT_PROOF'; + public const ECOMMERCE_PURCHASE_ORDER = 'ECOMMERCE_PURCHASE_ORDER'; + public const PROFORMA_INVOICE = 'PROFORMA_INVOICE'; public const PURCHASE_ORDER = 'PURCHASE_ORDER'; public const DELIVER_ORDER = 'DELIVER_ORDER'; public const INVOICE = 'INVOICE'; public const SUPPLIER_DELIVER_ORDER = 'SUPPLIER_DELIVER_ORDER'; + public const BULK_PURCHASE_ORDER = 'BULK_PURCHASE_ORDER'; } 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 @@ + 'Customer Paid', + 'description' => '', + 'milestone' => 'MILESTONE 1 - Customer Paid', + 'reference' => '', + 'on_task_completion' => '', + 'department' => '', + 'status' => PerfexCRMTaskStatus::COMPLETED, + 'priority' => PerfexCRMTaskPriority::DEFAULT, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + + public const TASK_1_DAY_TRANSFER_1 = [ + 'name' => 'Map Bank Transaction Record', + 'description' => '○ Purpose: To map a transaction to bank transaction in the bank statement
+ ○ Amount: RM {amount}
+ ○ Link to order page: {link_transfer}
', + 'milestone' => '', + 'reference' => 'TASK_1_DAY_TRANSFER_1', + 'on_task_completion' => 'TASK_1_DAY_TRANSFER_2', + 'department' => 'Accounts', + 'status' => PerfexCRMTaskStatus::IN_PROGRESS, + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1_DAY_TRANSFER_2 = [ + 'name' => 'Approve Payment', + 'description' => '○ Purpose: To verify and approve the customer\'s payment on exchange
+ ○ Initial Status: Not Started
+ ○ Deadline:If payment is created before 4:30 pm, it must be made on the same day. If payment is created after 4:30 pm, it must be made the next day
+ ○ Responsible department: Accounts
+ ○ Next step:
+ i. Change the status of the Issue Exchange Autocount Invoice operation to "In Progress"
+ ii. Change the status of the Order Placed in White Form operation to "In Progress" upon successful completion.
+ ○ Additional details: When the payment method is FPX or Wallet this task is performed automatically by the system.
+ ○ Dependencies: Map Transaction operation must be completed before this operation can begin.
+ ○ Outcomes: The payment will be approved in exchange, allowing the next steps in the process to be initiated.
', + 'milestone' => '', + 'reference' => 'TASK_1_DAY_TRANSFER_2', + 'on_task_completion' => 'TASK_1_DAY_TRANSFER_5', + 'department' => 'Accounts', + 'status' => PerfexCRMTaskStatus::IN_PROGRESS, + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 0, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => true + ]; + public const TASK_1_DAY_TRANSFER_3 = [ + 'name' => 'Issue Exchange Autocount Invoince', + 'description' => '○ Purpose: To issue an invoice for the customer\'s payment in accounting software.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Knockoff Invoice operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Payment operation must be completed before this operation can begin.
+ ○ Outcomes: An invoice will be issued in accounting software for the customer\'s payment.
', + 'milestone' => '', + 'reference' => 'TASK_1_DAY_TRANSFER_3', + 'on_task_completion' => 'TASK_1_DAY_TRANSFER_3_1', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1_DAY_TRANSFER_3_1 = [ + 'name' => 'Issue Exchange Autocount OR', + 'description' => '○ {link_autocount_or}
', + 'milestone' => '', + 'reference' => 'TASK_1_DAY_TRANSFER_3_1', + 'on_task_completion' => 'TASK_1_DAY_TRANSFER_4', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::DEFAULT, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1_DAY_TRANSFER_4 = [ + 'name' => 'Knockoff Invoice', + 'description' => '○ Purpose: The purpose of this operation is to issue the official receipt and knockoff with invoice for the customer\'s payment.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day.
+ ○ Responsible Department: Accounts
+ ○ Next Step: None
+ ○ Additional Details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Issue Exchange Autocount Invoice operation must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s payment is applied to the accounting software invoice and invoice is marked as paid.
', + 'milestone' => '', + 'reference' => 'TASK_1_DAY_TRANSFER_4', + 'on_task_completion' => 'TASK_1_DAY_TRANSFER_5', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1_DAY_TRANSFER_5 = [ + 'name' => 'Order Placed in White Form', + 'description' => '○ Purpose: To confirm that the order has been placed with the supplier.
+ ○ Amount: RM {amount}
+ ○ Currency: {currency}
+ ○ Service Type: {service_type}
+ ○ Bank-in details:
+ ○ Reference: {bank_details.reference}
+ ○ Account Holder Name: {bank_details.holder_name}
+ ○ Account No.: {bank_details.account_no}
+ ○ Bank Name: {bank_details.bank_name}
+ ○ Bank Branch: {bank_details.bank_branch}
', + 'milestone' => '', + 'reference' => 'TASK_1_DAY_TRANSFER_5', + 'on_task_completion' => 'TASK_1_DAY_TRANSFER_6', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 0, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => true + ]; + public const TASK_1_DAY_TRANSFER_6 = [ + 'name' => 'Upload China Bank Slip', + 'description' => '○ Purpose: To confirm that the customer order has been transferred to the customer\'s supplier.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day
+ ○ Responsible department: Operations
+ ○ Next step: None
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Order Placed in White Form operation must be completed before this operation can begin.
+ ○ Outcomes: The customer can download the payment transfer proof to send to their supplier.
', + 'milestone' => '', + 'reference' => 'TASK_1_DAY_TRANSFER_6', + 'on_task_completion' => '', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => true + ]; + + public const TASK_3_DAY_TRANSFER_1 = [ + 'name' => 'Map Bank Transaction Record', + 'description' => '○ Purpose: To map a transaction to bank transaction in the bank statement
+ ○ Initial Status: In Progress
+ ○ Deadline: If payment is created before 4:30 pm, it must be made on the same day. If payment is created after 4:30 pm, it must be made the next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Approve payment status to "In Progress" upon successful completion of the operation.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: None
+ ○ Outcomes: Bank transaction is mapped successfully, allowing the next steps in the process to be initiated.
', + 'milestone' => '', + 'reference' => 'TASK_3_DAY_TRANSFER_1', + 'on_task_completion' => 'TASK_3_DAY_TRANSFER_2', + 'department' => 'Accounts', + 'status' => PerfexCRMTaskStatus::IN_PROGRESS, + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_3_DAY_TRANSFER_2 = [ + 'name' => 'Approve Payment', + 'description' => '○ Purpose: To verify and approve the customer\'s payment on exchange
+ ○ Amount: RM {amount}
+ ○ Link to order page: {link_transfer}
', + 'milestone' => '', + 'reference' => 'TASK_3_DAY_TRANSFER_2', + 'on_task_completion' => 'TASK_3_DAY_TRANSFER_5', + 'department' => 'Accounts', + 'status' => PerfexCRMTaskStatus::IN_PROGRESS, + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 0, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => true + ]; + public const TASK_3_DAY_TRANSFER_3 = [ + 'name' => 'Issue Exchange Autocount Invoince', + 'description' => '○ Purpose: To issue an invoice for the customer\'s payment in accounting software.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Knockoff Invoice operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Payment operation must be completed before this operation can begin.
+ ○ Outcomes: An invoice will be issued in accounting software for the customer\'s payment.
', + 'milestone' => '', + 'reference' => 'TASK_3_DAY_TRANSFER_3', + 'on_task_completion' => 'TASK_3_DAY_TRANSFER_3_1', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_3_DAY_TRANSFER_3_1 = [ + 'name' => 'Issue Exchange Autocount OR', + 'description' => '○ {link_autocount_or}
', + 'milestone' => '', + 'reference' => 'TASK_3_DAY_TRANSFER_3_1', + 'on_task_completion' => 'TASK_3_DAY_TRANSFER_4', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::DEFAULT, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_3_DAY_TRANSFER_4 = [ + 'name' => 'Knockoff Invoice', + 'description' => '○ Purpose: The purpose of this operation is to issue the official receipt and knockoff with invoice for the customer\'s payment.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day.
+ ○ Responsible Department: Accounts
+ ○ Next Step: None
+ ○ Additional Details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Issue Exchange Autocount Invoice operation must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s payment is applied to the accounting software invoice and invoice is marked as paid.
', + 'milestone' => '', + 'reference' => 'TASK_3_DAY_TRANSFER_4', + 'on_task_completion' => 'TASK_3_DAY_TRANSFER_5', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_3_DAY_TRANSFER_5 = [ + 'name' => 'Order Placed in White Form', + 'description' => '○ Purpose: To confirm that the order has been placed with the supplier.
+ ○ Amount: RM {amount}
+ ○ Currency: {currency}
+ ○ Service Type: {service_type}
+ ○ Bank-in details:
+ ○ Reference: {bank_details.reference}
+ ○ Account Holder Name: {bank_details.holder_name}
+ ○ Account No.: {bank_details.account_no}
+ ○ Bank Name: {bank_details.bank_name}
+ ○ Bank Branch: {bank_details.bank_branch}
', + 'milestone' => '', + 'reference' => 'TASK_3_DAY_TRANSFER_5', + 'on_task_completion' => 'TASK_3_DAY_TRANSFER_6', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 0, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => true + ]; + public const TASK_3_DAY_TRANSFER_6 = [ + 'name' => 'Upload China Bank Slip', + 'description' => '○ Purpose: To confirm that the customer order has been transferred to the customer\'s supplier.
+ ○ Initial Status: Not Started
+ ○ Deadline: After 3 days
+ ○ Responsible department: Operations
+ ○ Next step: None
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Order Placed in White Form operation must be completed before this operation can begin.
+ ○ Outcomes: The customer can download the payment transfer proof to send to their supplier.
', + 'milestone' => '', + 'reference' => 'TASK_3_DAY_TRANSFER_6', + 'on_task_completion' => '', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => true + ]; + + + public const TASK_1688_PAYMENT_1 = [ + 'name' => 'Map Bank Transaction Record', + 'description' => '○ Purpose: To map a transaction to bank transaction in the bank statement
+ ○ Initial Status: In Progress
+ ○ Deadline: If payment is created before 4:30 pm, it must be made on the same day. If payment is created after 4:30 pm, it must be made the next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Approve payment status to "In Progress" upon successful completion of the operation.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: None
+ ○ Outcomes: Bank transaction is mapped successfully, allowing the next
+ steps in the process to be initiated.
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_1', + 'on_task_completion' => 'TASK_1688_PAYMENT_2', + 'department' => 'Accounts', + 'status' => PerfexCRMTaskStatus::IN_PROGRESS, + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_2 = [ + 'name' => 'Approve Payment', + 'description' => '○ Purpose: To verify and approve the customer\'s payment on exchange
+ ○ Initial Status: Not Started
+ ○ Deadline:If payment is created before 4:30 pm, it must be made on the same day. If payment is created after 4:30 pm, it must be made the next day
+ ○ Responsible department: Accounts
+ ○ Next step:
+ i. Change the status of the Issue Exchange Autocount Invoice operation to "In Progress"
+ ii. Change the status of the Order Placed in White Form operation to "In Progress" upon successful completion.
+ ○ Additional details: When the payment method is FPX or Wallet this task is performed automatically by the system.
+ ○ Dependencies: Map Transaction operation must be completed before this operation can begin.
+ ○ Outcomes: The payment will be approved in exchange, allowing the next steps in the process to be initiated.
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_2', + 'on_task_completion' => 'TASK_1688_PAYMENT_5', + 'department' => 'Accounts', + 'status' => PerfexCRMTaskStatus::IN_PROGRESS, + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_3 = [ + 'name' => 'Issue Exchange Autocount Invoice', + 'description' => '○ Purpose: To issue an invoice for the customer\'s payment in accounting software.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Knockoff Invoice operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Payment operation must be completed before this operation can begin.
+ ○ Outcomes: An invoice will be issued in accounting software for the customer\'s payment.
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_3', + 'on_task_completion' => 'TASK_1688_PAYMENT_3_1', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_3_1 = [ + 'name' => 'Issue Exchange Autocount OR', + 'description' => '○ Purpose: + ○ Outcomes: An invoice will be issued in accounting software for the customer\'s payment.
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_3_1', + 'on_task_completion' => 'TASK_1688_PAYMENT_4', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::DEFAULT, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_4 = [ + 'name' => 'Knockoff Invoice', + 'description' => '○ Purpose: The purpose of this operation is to issue the official receipt and knockoff with invoice for the customer\'s payment.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day.
+ ○ Responsible Department: Accounts
+ ○ Next Step: None
+ ○ Additional Details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Issue Exchange Autocount Invoice operation must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s payment is applied to the accounting software invoice and invoice is marked as paid.
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_4', + 'on_task_completion' => 'TASK_1688_PAYMENT_5', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_5 = [ + 'name' => 'Order Placed in White Form', + 'description' => '○ Purpose: To confirm that the order has been placed with the supplier.
+ ○ Initial Status: Not Started
+ ○ Deadline: Same Day
+ ○ Responsible department: Operations
+ ○ Next step: Change the status of Send White Form to Operation
+ Department operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Payment operation must be completed before this operation can begin.
+ ○ Outcomes: The order is placed with a supplier and the customer\'s order is confirmed.
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_5', + 'on_task_completion' => 'TASK_1688_PAYMENT_7', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_6 = [ + 'name' => 'Send White Form to Operation Department', + 'description' => '○ Purpose: To give confirmation to the operation department to process the order.
+ ○ Initial Status: Not Started
+ ○ Deadline: Same day
+ ○ Responsible department: Operations
+ ○ Next step: Change the status of Upload China Bank Slip operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Order Placed in White Form operation must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s order is confirmed and placed in a white form.
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_6', + 'on_task_completion' => 'TASK_1688_PAYMENT_7', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::DEFAULT, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_7 = [ + 'name' => 'Authorize Customer\'s 1688 Account', + 'description' => '○ Purpose: To authorize the alipay account to make payment to the customer\'s 1688 account.
+ ○ Initial Status: Not Started
+ ○ Deadline: Same day
+ ○ Responsible department: Operations
+ ○ Next step: Change the status of Make Payment for Customer 1688
+ Order operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Send White Form to Operation Department operation
+ must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s 1688 account is authorized to use the alipay account for making payments.
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_7', + 'on_task_completion' => 'TASK_1688_PAYMENT_8', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 1, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => true + ]; + public const TASK_1688_PAYMENT_8 = [ + 'name' => 'Make Payment for Customer 1688 Order', + 'description' => '○ Purpose: To confirm that the order has been placed with the supplier.
+ ○ 1688 Username: {bank_details.1688_username}
+ ○ 1688 Password: {bank_details.1688_password}
+ ○ Branch Code: {bank_details.payment_pin}
+ ○ Amount to Transfer: RM {amount}
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_8', + 'on_task_completion' => 'TASK_1688_PAYMENT_9', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::HIGH, + 'duedate' => 1, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => true + ]; + public const TASK_1688_PAYMENT_9 = [ + 'name' => 'Upload China Bank Slip', + 'description' => '○ Purpose: To confirm that the customer order has been transferred to the customer\'s supplier.
+ ○ Link to order page: {link_transfer}
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_9', + 'on_task_completion' => 'TASK_1688_PAYMENT_10', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => true + ]; + public const TASK_1688_PAYMENT_10 = [ + 'name' => 'Upload 1688 Purchase Order PDF', + 'description' => '○ Purpose: To store a copy of the original purchase order document for bookkeeping.
+ ○ Link to order page: {link_transfer}
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_10', + 'on_task_completion' => 'TASK_1688_PAYMENT_11', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::MEDIUM, + 'duedate' => 1, + 'is_allow_multiple' => true, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_11 = [ + 'name' => 'Fill Up Purchase Order', + 'description' => '○ Purpose: To store the purchase order details to generate the invoice.
+ ○ Link to order page: {link_transfer}
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_11', + 'on_task_completion' => 'TASK_1688_PAYMENT_12', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::LOW, + 'duedate' => 7, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_12 = [ + 'name' => 'Approve Purchase Order', + 'description' => '○ Purpose: To check that the customer submitted a purchase order that complies with our company’s guidelines.
+ ○ link: {link_transfer}
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_12', + 'on_task_completion' => '', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::LOW, + 'duedate' => 1, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + public const TASK_1688_PAYMENT_13 = [ + 'name' => 'Complete Order bookkeeping', + 'description' => '○ Purpose: To complete the bookkeeping for the booking.
+ ○ Initial Status: Not Started
+ ○ Deadline: Same day
+ ○ Responsible department: Operations
+ ○ Next step: None
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Purchase Order and Upload China Bank Slip operation must be completed before this operation can begin.
+ ○ Outcomes: The order is placed with a supplier and the customer’s order is confirmed.
', + 'milestone' => '', + 'reference' => 'TASK_1688_PAYMENT_13', + 'on_task_completion' => '', + 'department' => 'Operations', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::DEFAULT, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + + + + public const TASK_PURCHASE_ORDER_1 = [ + 'name' => 'Approve Purchase Order', + 'description' => '○ Purpose: To check that the customer submitted a purchase order that complies with our company’s guidelines.
+ ○ Initial Status: In Progress
+ ○ Deadline: Next day
+ ○ Responsible department: Operation
+ ○ Next step: Change the status of the Approve payment status to "In Progress" upon successful completion of the operation.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: None
+ ○ Outcomes: Bank transaction is mapped successfully, allowing the next steps in the process to be initiated.
', + 'milestone' => '', + 'reference' => 'TASK_PURCHASE_ORDER_1', + 'on_task_completion' => '', + 'department' => 'Operations', + 'status' => PerfexCRMTaskStatus::IN_PROGRESS, + 'priority' => PerfexCRMTaskPriority::LOW, + 'duedate' => 1, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + + public const TASK_PURCHASE_ORDER_2 = [ + 'name' => 'Complete Order bookkeeping', + 'description' => '○ Purpose: To complete the bookkeeping for the booking.
+ ○ Link to order page: {link_transfer}
', + 'milestone' => '', + 'reference' => 'TASK_PURCHASE_ORDER_2', + 'on_task_completion' => '', + 'department' => 'Accounts', + 'status' => '', + 'priority' => PerfexCRMTaskPriority::DEFAULT, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; + + + public const TASK_IDENTIFICATION_1 = [ + 'name' => 'Approve Identification Verification (Exchange)', + 'description' => '○ Purpose: To ensure the customer submit information matches the information in their identification document.
+ ○ Initial Status: In Progress
+ ○ Deadline: Same Day
+ ○ Responsible department: Operation
+ ○ Next step: None
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: None
+ ○ Outcomes: Customer account identification is verified
', + 'milestone' => '', + 'reference' => 'TASK_IDENTIFICATION_1', + 'on_task_completion' => '', + 'department' => 'Operations', + 'status' => PerfexCRMTaskStatus::IN_PROGRESS, + 'priority' => PerfexCRMTaskPriority::DEFAULT, + 'duedate' => 0, + 'is_allow_multiple' => false, + 'is_on_task_completion_update' => false + ]; +} diff --git a/app/Classes/ValueObjects/Constants/RewardCreationOptions.php b/app/Classes/ValueObjects/Constants/RewardCreationOptions.php new file mode 100644 index 00000000..c67dde49 --- /dev/null +++ b/app/Classes/ValueObjects/Constants/RewardCreationOptions.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 @@ + "Shadow Admin", + self::SUPER_ADMIN => "Super Admin", + self::ADMIN => "Admin", + self::USER => "User", + ]; + } \ No newline at end of file diff --git a/app/Classes/ValueObjects/Constants/SegmentConstants.php b/app/Classes/ValueObjects/Constants/SegmentConstants.php index dd6b093a..56f796cf 100644 --- a/app/Classes/ValueObjects/Constants/SegmentConstants.php +++ b/app/Classes/ValueObjects/Constants/SegmentConstants.php @@ -10,6 +10,8 @@ class SegmentConstants public const CUSTOM_SEGMENT = 2; + public const LABEL_SEGMENT = 3; + public const SYSTEM_PRIMARY_CURRENCY = 'SYSTEM_PRIMARY_CURRENCY'; public const SUPPLIER_CURRENCIES = 'SUPPLIER_CURRENCIES'; diff --git a/app/Classes/ValueObjects/Constants/ShippingTransactionType.php b/app/Classes/ValueObjects/Constants/ShippingTransactionType.php new file mode 100644 index 00000000..0668e8ca --- /dev/null +++ b/app/Classes/ValueObjects/Constants/ShippingTransactionType.php @@ -0,0 +1,39 @@ + self::EXCHANGE, + 'shipping_portal' => self::SHIPPING_PORTAL, + 'izyim' => self::SHIPPING_PORTAL, + ]; +} diff --git a/app/Classes/ValueObjects/Constants/TransactionType.php b/app/Classes/ValueObjects/Constants/TransactionType.php index 4814ac04..026f25ba 100644 --- a/app/Classes/ValueObjects/Constants/TransactionType.php +++ b/app/Classes/ValueObjects/Constants/TransactionType.php @@ -29,4 +29,6 @@ final class TransactionType { public const WITHDRAW = 10; public const TRANSFER_FEE = 12; + + public const CASH_BACK = 13; } 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 @@ +removesCompanyFromSegment = $removesCompanyFromSegment; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + $wallets = Wallet::all(); + + $i = 0; + foreach ($wallets as $wallet) { + $topups = 0; + $credit = 0; + $payments = 0; + $debit = 0; + + foreach ($wallet->transactions as $transaction) { + if (!in_array((int) $transaction->status, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])) continue; + if ((int) $transaction->type === TransactionType::TOP_UP) { + $topups += (float) $transaction->amount; + } + if ((int) $transaction->type === TransactionType::CREDIT_NOTE) $credit += (float) $transaction->amount; + if ((int) $transaction->type === TransactionType::PAYMENT) $payments += (float) $transaction->amount; + if ((int) $transaction->type === TransactionType::DEBIT_NOTE) $debit += (float) $transaction->amount; + } + $auditBalance = ($topups + $credit) - ($payments + $debit); + $diffenrence = round((float) $wallet->amount - (($topups + $credit) - ($payments + $debit)), 2); + if ((($diffenrence == 0) || ($diffenrence == -0)) and $wallet->amount > -0.01) continue; + + $i++; + + $this->info($i . ". Marking: " . $wallet->owner->reference . "(" . $wallet->id . ")" . PHP_EOL . "Current Balance: " . $wallet->amount . PHP_EOL . "Audit Balance: " . ($auditBalance) . PHP_EOL . "Difference: " . $diffenrence . PHP_EOL); + + Wallet::where('id', $wallet->id)->update(['amount' => $auditBalance]); + } + } +} diff --git a/app/Console/Commands/DeleteOrderCommand.php b/app/Console/Commands/DeleteOrderCommand.php new file mode 100644 index 00000000..9bcefe47 --- /dev/null +++ b/app/Console/Commands/DeleteOrderCommand.php @@ -0,0 +1,86 @@ +argument('bookings_reference'); + // $bookings_reference = '28546,38599,44487,71086,70133,58580,42831,96028,41188,33894,95877,86732,31894,50962,44215,92894,40968,30303,89762,74693,45271,27169'; + $bookings_reference = explode(',', $bookings_reference); + + $start = new Carbon(); + $this->logOutput('Process started'); + + foreach ($bookings_reference as $reference) { + $booking = Booking::where('marking', $reference)->first(); + + if (!$booking) { + $this->logOutput('Booking not found: ' . $reference); + } else { + $payment = $booking->transactions() + ->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->first(); + + $payment->status = ApprovalStatus::EXPIRED; + $payment->save(); + + $this->logOutput('Booking ' . $reference . ' Payment deleted: ' . $payment->id); + } + } + + $end = new Carbon(); + $elapsedTime = $start->diff($end)->format('%H:%I:%S'); + + $this->logOutput('Process ended. ElapsedTime: ' . $elapsedTime); + } + + public function logOutput($text) + { + if (is_array($text)) { + $text = implode(', ', $text); + } + + $this->info(Carbon::now() . ' : ' . $text); + + $filePath = storage_path('logs/delete-orders.log'); + $textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' ' . $text . PHP_EOL; + file_put_contents($filePath, $textToAppend, FILE_APPEND); + } +} diff --git a/app/Console/Commands/RemoveSeasonalSegmentCompany.php b/app/Console/Commands/RemoveSeasonalSegmentCompany.php new file mode 100644 index 00000000..be41d05d --- /dev/null +++ b/app/Console/Commands/RemoveSeasonalSegmentCompany.php @@ -0,0 +1,57 @@ +removesCompanyFromSegment = $removesCompanyFromSegment; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + $seasonalSegment = SeasonalSegment::where('ending_on', '<=', Carbon::today())->get(); + + if (count($seasonalSegment)){ + foreach ($seasonalSegment as $seasonalSegmentCompany) { + $this->removesCompanyFromSegment->execute($seasonalSegmentCompany->company, $seasonalSegmentCompany->segment); + $seasonalSegmentCompany->delete(); + $this->info(Carbon::now() . ' : Deleted id: ' . $seasonalSegmentCompany->id); + } + } + } +} diff --git a/app/Console/Commands/debugBillplzFailedPayment.php b/app/Console/Commands/debugBillplzFailedPayment.php new file mode 100644 index 00000000..ee1cf4b0 --- /dev/null +++ b/app/Console/Commands/debugBillplzFailedPayment.php @@ -0,0 +1,75 @@ +where('payment_method', PaymentMethodType::PAYMENT_GATEWAY)->whereNotIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED])->get(); + + $i = 0; + $totalAmount = 0; + foreach ($transactions as $transaction){ + $response = Http::withBasicAuth(config('billplz.api_key').':', '')->get(config('billplz.base_url').'/api/v3/bills/'.$transaction->payment_reference); + + // dd($response); + + if($response->successful()){ + $data = $response->json(); + if($data['paid']){ + if (!in_array($transaction->status, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])) { + $this->returnLog('Paid transaction', $transaction); + } + } + // else { + // $totalAmount += $transaction->amount; + // $this->returnLog('Unpaid Transaction', $transaction); + // } + }else{ + $this->returnLog('billplz error', $transaction); + } + } + } + + public function returnLog($text, $transaction) { + $approvalStatusArray = ApprovalStatus::APPROVAL_STATUS_ID; + $this->info($text . ' - id: '. $transaction->id . '. Booking Marking: '. $transaction->owner->marking . ' - Date: '.$transaction->created_at->format('d-m-Y').' - Amount: '. $transaction->amount . '. Current Status: ' . $approvalStatusArray[$transaction->status]); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 8edaaa22..e549a23a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -34,6 +34,10 @@ class Kernel extends ConsoleKernel // $schedule->command('inspire')->hourly(); $schedule->command('mail:EmailDoToVTCommand')->dailyAt('10:00')->withoutOverlapping(); + $schedule->command('seasonalSegmantCompany:remove') + ->dailyAt('01:00') + ->appendOutputTo(storage_path().'/logs/soft-delete-seasonal-segmant-company.log') + ->withoutOverlapping(); } /** diff --git a/app/Http/Controllers/Accounting/ApproveDuplicateBankStatementDetailsStatusController.php b/app/Http/Controllers/Accounting/ApproveDuplicateBankStatementDetailsStatusController.php new file mode 100644 index 00000000..0c026835 --- /dev/null +++ b/app/Http/Controllers/Accounting/ApproveDuplicateBankStatementDetailsStatusController.php @@ -0,0 +1,20 @@ +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/Accounts/FetchUserByEmailController.php b/app/Http/Controllers/Accounts/FetchUserByEmailController.php new file mode 100644 index 00000000..0a60d4c7 --- /dev/null +++ b/app/Http/Controllers/Accounts/FetchUserByEmailController.php @@ -0,0 +1,19 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Accounts/UpdateUserRoleController.php b/app/Http/Controllers/Accounts/UpdateUserRoleController.php new file mode 100644 index 00000000..aef992c8 --- /dev/null +++ b/app/Http/Controllers/Accounts/UpdateUserRoleController.php @@ -0,0 +1,19 @@ +execute($request); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Accounts/UserAuthenticationController.php b/app/Http/Controllers/Accounts/UserAuthenticationController.php index fdf4442d..1ce36699 100644 --- a/app/Http/Controllers/Accounts/UserAuthenticationController.php +++ b/app/Http/Controllers/Accounts/UserAuthenticationController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers\Accounts; use App\Classes\Modules\Accounts\ControllersLogic\AuthenticateUserLogic; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Log; +use TimeHunter\LaravelGoogleReCaptchaV3\Facades\GoogleReCaptchaV3; class UserAuthenticationController { diff --git a/app/Http/Controllers/Bookings/DeleteBookingController.php b/app/Http/Controllers/Bookings/DeleteBookingController.php index a5629052..dfbd8a01 100644 --- a/app/Http/Controllers/Bookings/DeleteBookingController.php +++ b/app/Http/Controllers/Bookings/DeleteBookingController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Bookings; -use App\Classes\Modules\Accounts\ControllersLogic\DeleteBookingLogic; +use App\Classes\Modules\Bookings\ControllersLogic\DeleteBookingLogic; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; diff --git a/app/Http/Controllers/Bookings/DeletePurchaseOrderPdfController.php b/app/Http/Controllers/Bookings/DeletePurchaseOrderPdfController.php new file mode 100644 index 00000000..0113c0d5 --- /dev/null +++ b/app/Http/Controllers/Bookings/DeletePurchaseOrderPdfController.php @@ -0,0 +1,16 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Bookings/DownloadBookingDocumentController.php b/app/Http/Controllers/Bookings/DownloadBookingDocumentController.php index d2a7d85c..95427363 100644 --- a/app/Http/Controllers/Bookings/DownloadBookingDocumentController.php +++ b/app/Http/Controllers/Bookings/DownloadBookingDocumentController.php @@ -15,7 +15,7 @@ class DownloadBookingDocumentController * @throws \App\Classes\Exceptions\MalformedRequestException */ public function download(Request $request, DownloadBookingDocumentLogic $logic) { - $logic->execute($request); + return $logic->execute($request); } } \ No newline at end of file diff --git a/app/Http/Controllers/Bookings/UpdateBookingOwnerController.php b/app/Http/Controllers/Bookings/UpdateBookingOwnerController.php new file mode 100644 index 00000000..1ced189c --- /dev/null +++ b/app/Http/Controllers/Bookings/UpdateBookingOwnerController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Bookings/UploadPurchaseOrderController.php b/app/Http/Controllers/Bookings/UploadPurchaseOrderController.php new file mode 100644 index 00000000..524691be --- /dev/null +++ b/app/Http/Controllers/Bookings/UploadPurchaseOrderController.php @@ -0,0 +1,16 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Companies/ListBusinessTypesController.php b/app/Http/Controllers/Companies/ListBusinessTypesController.php new file mode 100644 index 00000000..ecf1f0c9 --- /dev/null +++ b/app/Http/Controllers/Companies/ListBusinessTypesController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Companies/ListCompanyTypesController.php b/app/Http/Controllers/Companies/ListCompanyTypesController.php new file mode 100644 index 00000000..78c6abf9 --- /dev/null +++ b/app/Http/Controllers/Companies/ListCompanyTypesController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Companies/UpdateCompanyNameAndDebtorController.php b/app/Http/Controllers/Companies/UpdateCompanyNameAndDebtorController.php new file mode 100644 index 00000000..18599e7f --- /dev/null +++ b/app/Http/Controllers/Companies/UpdateCompanyNameAndDebtorController.php @@ -0,0 +1,19 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Companies/UpdateCompanyProfileController.php b/app/Http/Controllers/Companies/UpdateCompanyProfileController.php new file mode 100644 index 00000000..088c6ffd --- /dev/null +++ b/app/Http/Controllers/Companies/UpdateCompanyProfileController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Companies/UpdateCompanyStatusController.php b/app/Http/Controllers/Companies/UpdateCompanyStatusController.php new file mode 100644 index 00000000..43b3ba3c --- /dev/null +++ b/app/Http/Controllers/Companies/UpdateCompanyStatusController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Currencies/History/ListCurrencyRateHistory.php b/app/Http/Controllers/Currencies/History/ListCurrencyRateHistory.php new file mode 100644 index 00000000..7e7db955 --- /dev/null +++ b/app/Http/Controllers/Currencies/History/ListCurrencyRateHistory.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Exports/ExportAnalyticToExcelController.php b/app/Http/Controllers/Exports/ExportAnalyticToExcelController.php new file mode 100644 index 00000000..2a93a991 --- /dev/null +++ b/app/Http/Controllers/Exports/ExportAnalyticToExcelController.php @@ -0,0 +1,37 @@ +headers->set('Authorization', 'Bearer '.$token); + } + + public function bookingData(ExportsAnalyticBookingTransactions $exportsAnalyticBookingTransactions, Request $request){ + $response = $exportsAnalyticBookingTransactions->download('bookingData.csv', Excel::CSV, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } + + public function billingData(ExportsAnalyticBillingTransactions $exportsAnalyticBillingTransactions, Request $request){ + $response = $exportsAnalyticBillingTransactions->download('billingData.csv', Excel::CSV, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php index 39ea3dc5..74384157 100644 --- a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php +++ b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php @@ -5,10 +5,12 @@ namespace App\Http\Controllers\Exports; use App\Classes\Modules\Exports\Services\ExportsCustomers; use App\Classes\Modules\Exports\Services\ExportsTransactions; +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; @@ -54,4 +56,23 @@ class ExportCustomersToExcelController ob_end_clean(); 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(); + return $response; + } + + public function leadsData(ExportsLeadsTransactions $exportsLeadsTransactions, Request $request){ + $response = $exportsLeadsTransactions->download('leadsData.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } } \ No newline at end of file diff --git a/app/Http/Controllers/Imports/ImportBankRecordController.php b/app/Http/Controllers/Imports/ImportBankRecordController.php new file mode 100644 index 00000000..bd0272d2 --- /dev/null +++ b/app/Http/Controllers/Imports/ImportBankRecordController.php @@ -0,0 +1,157 @@ + 'MBB Cyber', + 1 => 'MBB SS2', + ]; + + $yes = 'Yes'; + $no = 'No'; + + $table = ''; + + $collection = Excel::toCollection(new ImportsBankRecord(), 'daily_transaction_nov.xlsx'); + + foreach ($collection as $key => $sheet){ + $branch = $branches[$key]; + foreach ($sheet as $row) { + $date = Carbon::instance(Date::excelToDateTimeObject($row['date'])); + $description = $row['description']; + $credit = (float) $row['credit']; + $debit = (float) $row['debit']; + $creditTransactions = []; + $debitTransactions = []; + + $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; + } + + $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; + } + } + + 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->addDays(1)->format('Y-m-d'); + if($reference = 'ATVANTIC'){ + $paymentDate = $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; + } + } + + + $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 .= ' + + + + + + + + + + + + '; + } + } + + $table .= '
'.implode('', $headers).'
'.$date->format('d-m-Y').''.$branch.''.$description.''.$credit.''.$debit.''.$row['pay_for'].''.$systemReference.''.$row['remarkreferences'].''.$multiple.''.$matches.''.$systemAmount.'
'; + + echo $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(); + } +} diff --git a/app/Http/Controllers/Imports/ImportHoneyTrapController.php b/app/Http/Controllers/Imports/ImportHoneyTrapController.php new file mode 100644 index 00000000..8f016ed4 --- /dev/null +++ b/app/Http/Controllers/Imports/ImportHoneyTrapController.php @@ -0,0 +1,133 @@ +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(); + + $returnArray = []; + // $segment_name = 'honey trap campaign'; + // $segment = Segment::where('name', $segment_name)->first(); + $input_segment_id = $request->input('segment_id'); + if (!$input_segment_id) { + $row['status'] = 'Failed'; + $row['message'] = 'segment_id cannot be empty'; + $returnArray[] = $row; + return response()->json($returnArray); + } + + $segment = Segment::where('id', $input_segment_id)->first(); + if (!$segment) { + $row['status'] = 'Failed'; + $row['message'] = 'Segment not found'; + $returnArray[] = $row; + return response()->json($returnArray); + } + + $segment_id = $segment->id; + + foreach ($excelRows as $row) { + + if (is_null($row['email']) || empty($row['email'])) { + continue; + } + + if (is_null($row['end_date']) || empty($row['end_date'])) { + // use csv import must have an end_date + $row['status'] = 'failed'; + $row['message'] = 'End Date is required'; + $returnArray[] = $row; + continue; + } + + $row['end_date'] = $end_date = $this->changeExcelDate($row['end_date']); + $start_date = $row['start_date'] ? $this->changeExcelDate($row['start_date']) : Carbon::now(); + $row['start_date'] = $start_date; + + $end_date = Carbon::parse($end_date); + // Check if the end_date is in the past + if ($end_date->isPast()) { + // end_date must be after today's date + $row['status'] = 'failed'; + $row['message'] = "End Date must be after today's date"; + $returnArray[] = $row; + continue; + } + + $user = User::where('email', $row['email'])->first(); + if (!$user) { + // if user not found + $row['status'] = 'failed'; + $row['message'] = 'Email not found'; + $returnArray[] = $row; + continue; + } + + $company = $user->company->first(); + if (!$company) { + // if company not found + $row['status'] = 'failed'; + $row['message'] = 'Company not found'; + $returnArray[] = $row; + continue; + } + + $company_seasonal_honey_trap_count = SeasonalSegment::where('company_id', $company->id)->where('segment_id', $segment_id)->get(); + if (count($company_seasonal_honey_trap_count)) { + // seasonal segment already exists + $row['status'] = 'failed'; + $row['message'] = 'Company is already in the Honey Trap Segment'; + $returnArray[] = $row; + continue; + } + + $seasonalSegmentObject = new SeasonalSegmentObject($company->id, $segment_id, $start_date, $end_date ?? null); + + (App()->make(createsSeasonalSegment::class))->execute($seasonalSegmentObject); + (App()->make(assignSegmentProcessor::class))->execute($company, $segment_id); + + // success added honey trap seasonal segment + $row['status'] = 'success'; + $row['message'] = ''; + $returnArray[] = $row; + continue; + } + + return response()->json($returnArray); + } + + 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/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/Imports/ImportUpdateDebtorController.php b/app/Http/Controllers/Imports/ImportUpdateDebtorController.php index 72f998d4..1acc7b11 100644 --- a/app/Http/Controllers/Imports/ImportUpdateDebtorController.php +++ b/app/Http/Controllers/Imports/ImportUpdateDebtorController.php @@ -8,9 +8,12 @@ use App\Classes\General\ExcelHandel; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Models\User; +use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Str; use Maatwebsite\Excel\Facades\Excel; +use Maatwebsite\Excel\Excel as ExcelFileTypes; class ImportUpdateDebtorController { @@ -24,4 +27,4 @@ class ImportUpdateDebtorController Excel::import(new ImportsDebtor(), json_decode($object->getFiles()[0])->file_info->original->file); return []; } -} \ No newline at end of file +} 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/Notifications/ListNotificationsController.php b/app/Http/Controllers/Notifications/ListNotificationsController.php new file mode 100644 index 00000000..58455fc2 --- /dev/null +++ b/app/Http/Controllers/Notifications/ListNotificationsController.php @@ -0,0 +1,19 @@ +execute($request); + } +} \ No newline at end of file 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/CreateBulkPurchaseOrderDocumentController.php b/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderDocumentController.php new file mode 100644 index 00000000..eeeff585 --- /dev/null +++ b/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderDocumentController.php @@ -0,0 +1,21 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderTransactionController.php b/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderTransactionController.php new file mode 100644 index 00000000..695b8487 --- /dev/null +++ b/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderTransactionController.php @@ -0,0 +1,21 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Transactions/DeleteGroupController.php b/app/Http/Controllers/Transactions/DeleteGroupController.php new file mode 100644 index 00000000..14ba54f9 --- /dev/null +++ b/app/Http/Controllers/Transactions/DeleteGroupController.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/Transactions/GenerateCreditNotePdfController.php b/app/Http/Controllers/Transactions/GenerateCreditNotePdfController.php new file mode 100644 index 00000000..11d27cdd --- /dev/null +++ b/app/Http/Controllers/Transactions/GenerateCreditNotePdfController.php @@ -0,0 +1,15 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Transactions/ListGroupsController.php b/app/Http/Controllers/Transactions/ListGroupsController.php new file mode 100644 index 00000000..730633e9 --- /dev/null +++ b/app/Http/Controllers/Transactions/ListGroupsController.php @@ -0,0 +1,21 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Transactions/UpdateGroupController.php b/app/Http/Controllers/Transactions/UpdateGroupController.php new file mode 100644 index 00000000..db0b3a17 --- /dev/null +++ b/app/Http/Controllers/Transactions/UpdateGroupController.php @@ -0,0 +1,20 @@ +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/Controllers/Wallets/WalletReportController.php b/app/Http/Controllers/Wallets/WalletReportController.php new file mode 100644 index 00000000..998bba53 --- /dev/null +++ b/app/Http/Controllers/Wallets/WalletReportController.php @@ -0,0 +1,29 @@ + [ + 'walletSum' => (float) Wallet::all()->sum('amount'), + 'outgoingSum' => (float) Transaction::where('type', TransactionType::PAYMENT)->where('owner_type', Wallet::class)->sum('amount'), + 'incomingSum' => (float) Transaction::whereIn('type', [TransactionType::TOP_UP, TransactionType::CREDIT_NOTE])->where('owner_type', Wallet::class)->sum('amount'), + ]] + ))->handler(); + } +} diff --git a/app/Http/Resources/AnnouncementResource.php b/app/Http/Resources/AnnouncementResource.php index 26e1c2f9..2e6048e0 100644 --- a/app/Http/Resources/AnnouncementResource.php +++ b/app/Http/Resources/AnnouncementResource.php @@ -20,8 +20,8 @@ class AnnouncementResource extends JsonResource 'title' => $this->title, 'description' => $this->description, 'segments' => $this->segments, - 'starting_on' => Carbon::parse($this->starting_on)->format('Y-m-d'), - 'ending_on' => Carbon::parse($this->ending_on)->format('Y-m-d'), + 'starting_on' => Carbon::parse($this->starting_on)->format('d-m-Y'), + 'ending_on' => Carbon::parse($this->ending_on)->format('d-m-Y'), 'created_at' => Carbon::parse($this->created_at)->format('d-m-Y'), ]; } diff --git a/app/Http/Resources/BankResource.php b/app/Http/Resources/BankResource.php index 35fec923..f3de3038 100644 --- a/app/Http/Resources/BankResource.php +++ b/app/Http/Resources/BankResource.php @@ -21,6 +21,7 @@ class BankResource extends JsonResource 'reference' => $this->reference, 'bank_name' => $this->bank_name, 'bank_branch' => $this->bank_branch, + 'swift' => $this->swift, 'holder_name' => $this->holder_name, 'account_no' => $this->account_no, 'country_id' => $this->country_id, 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/BookingResource.php b/app/Http/Resources/BookingResource.php index 0eeb9670..f3a7881d 100644 --- a/app/Http/Resources/BookingResource.php +++ b/app/Http/Resources/BookingResource.php @@ -11,6 +11,7 @@ use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\ValueObjects\Constants\DocumentType; use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Support\Facades\Log; class BookingResource extends JsonResource { @@ -43,6 +44,7 @@ class BookingResource extends JsonResource 'invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::INVOICE)->first()), 'supplier_delivery_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::SUPPLIER_DELIVER_ORDER)->first()), 'proforma_invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::PROFORMA_INVOICE)->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::EXPIRED])->orderByDesc('id')->first()), + 'ecommerce_purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->first()), ], 'status' => $this->status, 'created_at' => Carbon::parse($this->created_at)->format('d-m-Y'), @@ -55,7 +57,7 @@ class BookingResource extends JsonResource ->whereDate('expires_on', '>=', Carbon::now()) ->get() ), - 'expired_payment_attempts' => TransactionResource::collection($this->transactions()->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '<', Carbon::now())->get()), + 'expired_payment_attempts' => TransactionResource::collection($this->transactions()->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString())->get()), 'payment_history' => TransactionResource::collection($this->transactions()->where(function($query){ $query->where(function($query){ $query->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::COMPLETED, ApprovalStatus::REJECTED]); diff --git a/app/Http/Resources/CompanyResource.php b/app/Http/Resources/CompanyResource.php index fceab5bc..85ecee16 100644 --- a/app/Http/Resources/CompanyResource.php +++ b/app/Http/Resources/CompanyResource.php @@ -37,6 +37,7 @@ class CompanyResource extends JsonResource 'id' => $this->id, 'name' => $this->name, 'reference' => $this->reference, + 'debtor' => $this->debtor, 'type' => (int) $this->type, 'business_type' => (int) $this->business_type, 'status' => (int) $this->status, @@ -60,8 +61,9 @@ class CompanyResource extends JsonResource 'default' => new BankResource($this->banks->where('type', BankAccountType::EXTERNAL)->where('default', true)->first()) ], 'segments' => SegmentResource::collection($this->segments), + 'seasonalSegment' => $this->whenLoaded('seasonalSegments', SeasonalSegmentResource::collection($this->seasonalSegments)), 'services' => (new FetchesCompanyServices())->getServices($this->servicesConfigurations()), - 'wallet' => new WalletResource($this->wallets()->first()), + 'wallet' => $this->whenLoaded('wallets', new WalletResource($this->wallets()->with('transactions')->first()), new WalletResource($this->wallets()->first())), 'created_at' => $this->created_at->format('d-m-Y'), $this->mergeWhen($this->business_type === BusinessType::CURRENCY_VENDOR, [ 'currencies' => $segment ? CurrencyResource::collection(Currency::whereIn('id', $segment->detail->currencies)->get()) : [], diff --git a/app/Http/Resources/CurrencyRateLogResource.php b/app/Http/Resources/CurrencyRateLogResource.php new file mode 100644 index 00000000..f8560dd8 --- /dev/null +++ b/app/Http/Resources/CurrencyRateLogResource.php @@ -0,0 +1,26 @@ +currencyRate; + + return [ + 'currency_rate_id' => $this->currency_rate_id, + 'rate' => $this->selling, + 'created_at' => $this->created_at->format('d-m-Y'), + 'payment_method_type' => $currencyRate->payment_method_type, + ]; + } +} diff --git a/app/Http/Resources/GeneralTypeResource.php b/app/Http/Resources/GeneralTypeResource.php new file mode 100644 index 00000000..b6dc2348 --- /dev/null +++ b/app/Http/Resources/GeneralTypeResource.php @@ -0,0 +1,22 @@ + $this->id, + 'type' => $this->type, + ]; + } +} diff --git a/app/Http/Resources/GroupResource.php b/app/Http/Resources/GroupResource.php new file mode 100644 index 00000000..abd017ec --- /dev/null +++ b/app/Http/Resources/GroupResource.php @@ -0,0 +1,53 @@ +issuerCompany){ + dd($this->id); + } + return [ + 'id' => $this->id, + 'original_amount' => (float) $this->original_amount, + 'original_currency' => new CurrencyResource($this->original_currency), + 'issuer_name' => $this->issuerCompany->name, + 'issuer_id' => $this->issuerCompany->id, + 'amount' => (float) $this->amount, + 'service_charge' => (float) $this->amount, + 'currency' => new CurrencyResource($this->currency), + 'created_at' => Carbon::parse($this->created_at)->format('d-m-Y h:i:s A'), + 'currency_rate' => (float) $this->currency_rate, + 'transactions' => $this->transactions()->get()->pluck('owner.owner.marking'), + 'complete_transactions' => $this->transactions()->whereHasMorph('owner', [Transaction::class], function($query){ + return $query->whereHas('booking', function($query){ + return $query->whereHas('transactions', function($query){ + return $query->where('type', TransactionType::PURCHASE_ORDER)->where('status', '=', ApprovalStatus::APPROVED); + }); + }); + })->get()->pluck('owner.owner.marking'), + 'documents' => [ + 'currency_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::CURRENCY_VENDOR_ORDER)->first()), + 'purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::BULK_PURCHASE_ORDER)->first()) + ] + ]; + } +} 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/NotificationResource.php b/app/Http/Resources/NotificationResource.php new file mode 100644 index 00000000..564b7b31 --- /dev/null +++ b/app/Http/Resources/NotificationResource.php @@ -0,0 +1,30 @@ + $this->id, + 'title' => $this->title, + 'description' => $this->description, + 'long_ago' => $this->created_at->diffForHumans(), + 'created_at' => $this->created_at->format('d-m-Y') + ]; + + } +} 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/SeasonalSegmentResource.php b/app/Http/Resources/SeasonalSegmentResource.php new file mode 100644 index 00000000..8a38f5be --- /dev/null +++ b/app/Http/Resources/SeasonalSegmentResource.php @@ -0,0 +1,37 @@ + $this->id, + 'segment_name' => ucwords($this->segment->name), + 'ending_on' => $this->ending_on->format('d-m-Y'), + ]; + } +} diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php index 30ba223c..27fd17ab 100644 --- a/app/Http/Resources/TransactionResource.php +++ b/app/Http/Resources/TransactionResource.php @@ -30,6 +30,7 @@ class TransactionResource extends JsonResource '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), @@ -47,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/UserCompanyResource.php b/app/Http/Resources/UserCompanyResource.php new file mode 100644 index 00000000..09e9f2b7 --- /dev/null +++ b/app/Http/Resources/UserCompanyResource.php @@ -0,0 +1,25 @@ + $this->id, + 'name' => $this->name, + 'reference' => $this->company()->first()->reference, + 'type' => (int) $this->type, + 'status' => (int) $this->status + ]; + } +} diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index 3d052d02..97f4ab38 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -2,6 +2,7 @@ namespace App\Http\Resources; +use App\Classes\ValueObjects\Constants\RoleTypes; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource @@ -14,11 +15,14 @@ class UserResource extends JsonResource */ public function toArray($request) { + $userTypeArray = RoleTypes::USER_TYPE_ID; + return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, 'type' => (int) $this->type, + 'type_name' => ($userTypeArray[(int) $this->type]), 'status' => (int) $this->status ]; } 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/Http/Resources/WalletResource.php b/app/Http/Resources/WalletResource.php index 143af1c4..4fa484ad 100644 --- a/app/Http/Resources/WalletResource.php +++ b/app/Http/Resources/WalletResource.php @@ -21,8 +21,8 @@ class WalletResource extends JsonResource 'currency_id' => $this->currency_id, 'amount' => (double) $this->amount, 'company_id' => (int) $this->owner->id, - 'transactions' => WalletTransactionResource::collection($this->transactions()->whereIn('status', [2, 3])->orderBy('id', 'DESC')->get()), - 'top_up_records' => WalletTransactionResource::collection($this->transactions()->whereNotIn('status', [0])->where('type', TransactionType::TOP_UP)->orderBy('id', 'DESC')->get()) + 'transactions' => $this->whenLoaded('transactions', WalletTransactionResource::collection($this->transactions()->whereIn('status', [2, 3])->orderBy('id', 'DESC')->get()), []), + 'top_up_records' => $this->whenLoaded('transactions', WalletTransactionResource::collection($this->transactions()->whereNotIn('status', [0])->where('type', TransactionType::TOP_UP)->orderBy('id', 'DESC')->get()), []), ]; } } diff --git a/app/Http/Resources/WalletTransactionResource.php b/app/Http/Resources/WalletTransactionResource.php index cfc8ab10..a0e1a930 100644 --- a/app/Http/Resources/WalletTransactionResource.php +++ b/app/Http/Resources/WalletTransactionResource.php @@ -7,6 +7,7 @@ use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Transaction; use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Support\Facades\Log; class WalletTransactionResource extends JsonResource { @@ -27,7 +28,14 @@ class WalletTransactionResource extends JsonResource $description = 'Credit Voucher for '.$this->payment_reference; break; case 1: - $marking = Transaction::where('payment_reference', $this->bill_no)->first()->owner->marking; + $booking = Transaction::where('payment_reference', $this->bill_no)->first()->owner; + + if(!$booking) { + $description = 'Payment for unknown booking, please contact tech support.'; + break; + } + + $marking = $booking->marking; $description = 'Payment For booking refs.'.''.$marking.''; break; case 11: @@ -37,6 +45,7 @@ class WalletTransactionResource extends JsonResource } return [ + 'id' => (int) $this->id, 'type' => (int) $this->type, 'marking' => $this->owner->owner->reference, 'bill_no' => $this->bill_no, diff --git a/app/Models/AbstractModel.php b/app/Models/AbstractModel.php index 7a7c9c06..47d8754b 100644 --- a/app/Models/AbstractModel.php +++ b/app/Models/AbstractModel.php @@ -3,11 +3,37 @@ namespace App\Models; +use App\Classes\General\Interfaces\Notifiable; use Illuminate\Database\Eloquent\Model; use Spatie\Activitylog\Traits\LogsActivity; +use Illuminate\Database\Eloquent\Relations\MorphTo; -class AbstractModel extends Model +class AbstractModel extends Model implements Notifiable { use LogsActivity; protected static $logFillable = true; + + /** + * @return MorphTo + */ + public function subject(): MorphTo + { + return $this->MorphTo('subject'); + } + + /** + * @return MorphTo + */ + public function target(): MorphTo + { + return $this->MorphTo('target'); + } + + /** + * @return MorphTo + */ + public function causer(): MorphTo + { + return $this->MorphTo('causer'); + } } \ No newline at end of file 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/BankLog.php b/app/Models/BankLog.php new file mode 100644 index 00000000..a516d151 --- /dev/null +++ b/app/Models/BankLog.php @@ -0,0 +1,10 @@ +BelongsTo(Company::class, 'company_id'); + return $this->BelongsTo(Company::class, 'company_id')->withTrashed(); } /** diff --git a/app/Models/Company.php b/app/Models/Company.php index def72720..8b2e6081 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -62,6 +62,14 @@ class Company extends AbstractModel implements Documentable return $this->belongsToMany(Segment::class, (new SegmentCompany())->getTable(), 'company_id', 'segment_id'); } + /** + * @return belongsToMany + */ + public function seasonalSegments() + { + return $this->hasMany(SeasonalSegment::class); + } + /** * @return belongsToMany */ diff --git a/app/Models/CurrencyRateLog.php b/app/Models/CurrencyRateLog.php index 38130956..85b0eecf 100644 --- a/app/Models/CurrencyRateLog.php +++ b/app/Models/CurrencyRateLog.php @@ -1,10 +1,19 @@ BelongsTo(CurrencyRate::class, 'currency_rate_id', 'id'); + } } 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/Group.php b/app/Models/Group.php new file mode 100644 index 00000000..01dd257e --- /dev/null +++ b/app/Models/Group.php @@ -0,0 +1,63 @@ +belongsToMany(Transaction::class, GroupTransaction::class); + } + + /** + * @return MorphMany + */ + public function documents(): morphMany + { + return $this->morphMany(Document::class, 'owner'); + } + + /** + * @return BelongsTo + */ + public function currency(): BelongsTo + { + return $this->BelongsTo(Currency::class, 'currency_id', 'id'); + } + + + /** + * @return HasManyDeep + */ + public function transferFees(): HasManyDeep + { + return $this->HasManyDeep(Transaction::class, [GroupTransaction::class, Transaction::class.' as alias'], ['group_id', ['owner_type', 'owner_id'], ['owner_type', 'owner_id']], ['id', null, null]); + } + + /** + * @return BelongsTo + */ + public function issuerCompany(): BelongsTo + { + return $this->BelongsTo( Company::class, 'issuer', 'id'); + } + + /** + * @return BelongsTo + */ + public function original_currency(): BelongsTo + { + return $this->BelongsTo(Currency::class, 'original_currency_id', 'id'); + } +} diff --git a/app/Models/GroupTransaction.php b/app/Models/GroupTransaction.php new file mode 100644 index 00000000..9ab72d18 --- /dev/null +++ b/app/Models/GroupTransaction.php @@ -0,0 +1,27 @@ +BelongsTo(Group::class, 'group_id', 'id'); + } + + /** + * @return BelongsTo + */ + public function transaction(): BelongsTo + { + return $this->BelongsTo(Transaction::class, 'transaction_id', 'id'); + } +} 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 @@ +BelongsTo(Package::class, 'package_id', 'id'); + } +} diff --git a/app/Models/Reward.php b/app/Models/Reward.php new file mode 100644 index 00000000..7dbdc5a9 --- /dev/null +++ b/app/Models/Reward.php @@ -0,0 +1,27 @@ +belongsToMany(Milestone::class, MilestoneReward::class); + } + + /** + * @return HasMany + */ + public function users(): HasMany + { + return $this->HasMany(UserReward::class, 'reward_id', 'id'); + } +} diff --git a/app/Models/SeasonalSegment.php b/app/Models/SeasonalSegment.php new file mode 100644 index 00000000..30bb1796 --- /dev/null +++ b/app/Models/SeasonalSegment.php @@ -0,0 +1,48 @@ +BelongsTo(Company::class, 'company_id', 'id'); + } + + public function segment() + { + return $this->belongsTo(Segment::class); + } +} 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 429449a6..05fbfde7 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -4,6 +4,8 @@ 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; use Carbon\Carbon; @@ -18,10 +20,15 @@ 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' + ]; protected $table = 'transactions'; @@ -102,6 +109,22 @@ class Transaction extends AbstractModel implements Documentable, Transactionable return $this->HasMany(TransactionDetail::class, 'transaction_id', 'id'); } + /** + * @return HasOne + */ + public function groupTransaction(): HasOne + { + return $this->HasOne(GroupTransaction::class, 'transaction_id'); + } + + /** + * @return HasOne + */ + public function voucherRedemption(): HasOne + { + return $this->HasOne(VoucherRedemption::class, 'transaction_id', 'id'); + } + public function convert_original_amount() { if($this->booking()->first()->fix_currency_id !== 1) { @@ -178,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/app/Models/Wallet.php b/app/Models/Wallet.php index 8c3114fd..00677513 100644 --- a/app/Models/Wallet.php +++ b/app/Models/Wallet.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Classes\General\Interfaces\Transactionable; +use App\Classes\General\Traits\LogData; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; @@ -12,6 +13,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany; class Wallet extends AbstractModel implements Transactionable { use SoftDeletes; + use LogData; protected $table = 'wallets'; /** diff --git a/composer.json b/composer.json index 32f52d83..b8b505d5 100644 --- a/composer.json +++ b/composer.json @@ -10,30 +10,37 @@ "require": { "php": "^7.2.5", "ext-fileinfo": "*", - "ext-json": "^1.6", + "ext-json": "*", "ext-zip": "*", "barryvdh/laravel-dompdf": "^0.9.0", "carlos-meneses/laravel-mpdf": "^2.1", "doctrine/dbal": "^2.12.1", "fideloper/proxy": "^4.2", "fruitcake/laravel-cors": "^1.0", - "guzzlehttp/guzzle": "^6.3", + "guzzlehttp/guzzle": "^7.0.1", "intervention/image": "^2.5", - "laravel/framework": "^7.0", + "laravel/framework": "^8.0", "laravel/tinker": "^2.0", "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", "staudenmeir/eloquent-has-many-deep": "^1.7", - "tymon/jwt-auth": "^1.0" + "timehunter/laravel-google-recaptcha-v3": "~2.5", + "tymon/jwt-auth": "^1.0", + "webklex/laravel-pdfmerger": "^1.3", + "ext-bcmath": "*" }, "require-dev": { - "facade/ignition": "^2.0", + "facade/ignition": "^2.3.6", "fzaninotto/faker": "^1.9.1", + "laravel/dusk": "^6.23", "mockery/mockery": "^1.3.1", - "nunomaduro/collision": "^4.1", - "phpunit/phpunit": "^8.5" + "nunomaduro/collision": "^5.0", + "phpunit/phpunit": "^9.0" }, "config": { "optimize-autoloader": true, @@ -52,6 +59,9 @@ "classmap": [ "database/seeds", "database/factories" + ], + "files": [ + "app/Classes/General/VoucherifyHelper.php" ] }, "autoload-dev": { diff --git a/config/app.php b/config/app.php index c080002d..fd0bbe8f 100644 --- a/config/app.php +++ b/config/app.php @@ -178,7 +178,9 @@ return [ // Third Parties Spatie\Permission\PermissionServiceProvider::class, Barryvdh\DomPDF\ServiceProvider::class, - Meneses\LaravelMpdf\LaravelMpdfServiceProvider::class + Mccarlosen\LaravelMpdf\LaravelMpdfServiceProvider::class, + TimeHunter\LaravelGoogleReCaptchaV3\Providers\GoogleReCaptchaV3ServiceProvider::class, + Webklex\PDFMerger\Providers\PDFMergerServiceProvider::class ], @@ -232,7 +234,9 @@ return [ 'Validator' => Illuminate\Support\Facades\Validator::class, 'View' => Illuminate\Support\Facades\View::class, 'PDF' => Barryvdh\DomPDF\Facade::class, - 'MPDF' => Meneses\LaravelMpdf\Facades\LaravelMpdf::class, + 'MPDF' => Mccarlosen\LaravelMpdf\Facades\LaravelMpdf::class, + 'GoogleReCaptchaV3'=> TimeHunter\LaravelGoogleReCaptchaV3\Facades\GoogleReCaptchaV3::class, + 'PDFMerger' => Webklex\PDFMerger\Facades\PDFMergerFacade::class ], diff --git a/config/database.php b/config/database.php index aeabec6f..ac0b718b 100644 --- a/config/database.php +++ b/config/database.php @@ -63,6 +63,26 @@ return [ ]) : [], ], + 'dusk' => [ + 'driver' => 'mysql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => false, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + 'pgsql' => [ 'driver' => 'pgsql', 'url' => env('DATABASE_URL'), diff --git a/config/googlerecaptchav3.php b/config/googlerecaptchav3.php new file mode 100644 index 00000000..29f3b1aa --- /dev/null +++ b/config/googlerecaptchav3.php @@ -0,0 +1,166 @@ + 'curl', + /* + |-------------------------------------------------------------------------- + | Enable/Disable Service + |-------------------------------------------------------------------------- + | Type: bool + | + | This option is used to disable/enable the service + | + | Supported: true, false + | + */ + 'is_service_enabled' => true, + /* + |-------------------------------------------------------------------------- + | Host Name + |-------------------------------------------------------------------------- + | Type: string + | Default will be empty, assign value only if you want domain check with Google response + | Google reCAPTCHA host name, https://www.google.com/recaptcha/admin + | + */ + 'host_name' => '', + /* + |-------------------------------------------------------------------------- + | Secret Key + |-------------------------------------------------------------------------- + | Type: string + | Google reCAPTCHA credentials, https://www.google.com/recaptcha/admin + | + */ + 'secret_key' => env('RECAPTCHA_V3_SECRET_KEY', ''), + /* + |-------------------------------------------------------------------------- + | Site Key + |-------------------------------------------------------------------------- + | Type: string + | Google reCAPTCHA credentials, https://www.google.com/recaptcha/admin + | + */ + 'site_key' => env('RECAPTCHA_V3_SITE_KEY', ''), + + /* + |-------------------------------------------------------------------------- + | Badge Style + |-------------------------------------------------------------------------- + | Type: boolean + | Support: + | - true: the badge will be shown inline within the form, also you can customise your style + | - false: the badge will be shown in the bottom right side + | + */ + 'inline' => false, + + /* + |-------------------------------------------------------------------------- + | Background Badge Style + |-------------------------------------------------------------------------- + | Type: boolean + | Support: + | - true: the background badge will be displayed at the bottom right of page + | - false: the background badge will be invisible + | + */ + 'background_badge_display' => false, + /* + |-------------------------------------------------------------------------- + | Background Mode + |-------------------------------------------------------------------------- + | Type: boolean + | Support: + | - true: the script will run on every page if you put init() on the global page + | - false: the script will only be running if there is action defined + | + */ + 'background_mode' => true, + + /* + |-------------------------------------------------------------------------- + | Score Comparision + |-------------------------------------------------------------------------- + | Type: bool + | If you enable it, the package will do score comparision from your setting + */ + 'is_score_enabled' => true, + /* + |-------------------------------------------------------------------------- + | Setting + |-------------------------------------------------------------------------- + | Type: array + | Define your score threshold, define your action + | action: Google reCAPTCHA required parameter + | threshold: score threshold + | score_comparison: true/false, if this is true, the system will do score comparision against your threshold for the action + */ + 'setting' => [ + [ + 'action' => 'login', + 'threshold' => 0.6, + 'score_comparison' => true, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Setting + |-------------------------------------------------------------------------- + | Type: array + | Define a list of ip that you want to skip + */ + 'skip_ips' => [ + + ], + /* + |-------------------------------------------------------------------------- + | Options + |-------------------------------------------------------------------------- + | Custom option field for your request setting, which will be used for RequestClientInterface + | + */ + 'options' => [ + + ], + /* + |-------------------------------------------------------------------------- + | API JS Url + |-------------------------------------------------------------------------- + | Type: string + | Google reCAPTCHA API JS URL + | use: + */ + 'api_js_url' => 'https://www.google.com/recaptcha/api.js', + /* + |-------------------------------------------------------------------------- + | Site Verify Url + |-------------------------------------------------------------------------- + | Type: string + | Google reCAPTCHA API + | please use "www.recaptcha.net" in your code in circumstances when "www.google.com" is not accessible. e.g China + | e.g. https://www.recaptcha.net/recaptcha/api.js + */ + 'site_verify_url' => 'https://www.google.com/recaptcha/api/siteverify', + + /* + |-------------------------------------------------------------------------- + | Language + |-------------------------------------------------------------------------- + | Type: string + | https://developers.google.com/recaptcha/docs/language + */ + 'language' => 'en', +]; diff --git a/config/perfexcrm.php b/config/perfexcrm.php new file mode 100644 index 00000000..7644a489 --- /dev/null +++ b/config/perfexcrm.php @@ -0,0 +1,7 @@ + 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/2020_11_29_212614_create_companies_wallet_table.php b/database/migrations/2020_11_29_212614_create_companies_wallet_table.php index 7a7ade13..3837a0e1 100644 --- a/database/migrations/2020_11_29_212614_create_companies_wallet_table.php +++ b/database/migrations/2020_11_29_212614_create_companies_wallet_table.php @@ -15,15 +15,13 @@ class CreateCompaniesWalletTable extends Migration { Schema::create('wallets', function (Blueprint $table) { $table->id(); - - $table->foreignId('company_id')->unsigned(); + $table->morphs('owner'); $table->string('code'); $table->foreignId('currency_id')->unsigned(); $table->decimal('amount', 20, 5)->default(0.00); $table->softDeletes(); $table->timestamps(); - $table->foreign('company_id')->references('id')->on('companies'); $table->foreign('currency_id')->references('id')->on('currencies'); }); } diff --git a/database/migrations/2020_12_01_102314_create_transactions_table.php b/database/migrations/2020_12_01_102314_create_transactions_table.php index a07bf413..366b9a27 100644 --- a/database/migrations/2020_12_01_102314_create_transactions_table.php +++ b/database/migrations/2020_12_01_102314_create_transactions_table.php @@ -17,7 +17,7 @@ class CreateTransactionsTable extends Migration { Schema::create('transactions', function (Blueprint $table) { $table->id(); - $table->foreignId('booking_id')->unsigned(); + $table->morphs('owner'); $table->string('type')->default(TransactionType::PAYMENT); $table->foreignId('issuer')->unsigned(); $table->foreignId('receiver')->unsigned(); @@ -37,13 +37,12 @@ class CreateTransactionsTable extends Migration $table->softDeletes(); $table->timestamps(); - $table->foreign('booking_id')->references('id')->on('bookings'); $table->foreign('currency_id')->references('id')->on('currencies'); $table->foreign('issuer')->references('id')->on('companies'); $table->foreign('receiver')->references('id')->on('companies'); $table->foreign('recipient_bank_account_id')->references('id')->on('banks'); $table->foreign('original_currency_id')->references('id')->on('currencies'); - + }); } diff --git a/database/migrations/2020_12_01_212314_create_wallet_transaction_table.php b/database/migrations/2020_12_01_212314_create_wallet_transaction_table.php deleted file mode 100644 index f87780c7..00000000 --- a/database/migrations/2020_12_01_212314_create_wallet_transaction_table.php +++ /dev/null @@ -1,51 +0,0 @@ -id(); - - $table->foreignId('wallet_id')->unsigned(); - $table->foreignId('transaction_id')->unsigned()->nullable(); - $table->foreignId('bill_no')->unsigned(); - $table->integer('type')->default(TransactionType::PAYMENT); - $table->decimal('amount', 14, 5)->default(0.00); - $table->foreignId('currency_id')->unsigned(); - $table->decimal('original_amount', 14, 5)->default(0.00); - $table->foreignId('original_currency_id')->unsigned(); - $table->decimal('currency_rate', 14, 5)->default(0.00); - $table->integer('status')->default(ApprovalStatus::PENDING_SUBMISSION); - $table->softDeletes(); - $table->timestamps(); - - $table->foreign('transaction_id')->references('id')->on('transactions'); - $table->foreign('wallet_id')->references('id')->on('wallets'); - $table->foreign('currency_id')->references('id')->on('currencies'); - $table->foreign('original_currency_id')->references('id')->on('currencies'); - - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('wallet_transaction'); - } -} diff --git a/database/migrations/2020_12_02_131123_create_receipt_details_table.php b/database/migrations/2020_12_02_131123_create_receipt_details_table.php deleted file mode 100644 index 0a008293..00000000 --- a/database/migrations/2020_12_02_131123_create_receipt_details_table.php +++ /dev/null @@ -1,38 +0,0 @@ -id(); - $table->foreignId('receipt_id')->unsigned(); - $table->decimal('price', 14, 5)->default(0.00); - $table->decimal('amount', 14, 5)->default(0.00); - $table->timestamps(); - - $table->foreign('receipt_id')->references('id')->on('receipts'); - - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('receipt_detail'); - } -} diff --git a/database/migrations/2021_10_02_082442_alter_wallet_company_id.php b/database/migrations/2021_10_02_082442_alter_wallet_company_id.php deleted file mode 100644 index c1eba98a..00000000 --- a/database/migrations/2021_10_02_082442_alter_wallet_company_id.php +++ /dev/null @@ -1,44 +0,0 @@ -dropForeign('wallets_company_id_foreign'); - $table->dropColumn('company_id'); - }); - } - - if (!Schema::hasColumn('wallets', 'owner_id')) { - Schema::table('wallets', function (Blueprint $table) { - $table->morphs('owner'); - }); - - //In-case the model name lengthy - Schema::table('wallets', function (Blueprint $table) { - $table->string('owner_type', 250)->change(); - }); - } - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - // - } -} diff --git a/database/migrations/2021_10_02_083144_alter_transaction_booking_id.php b/database/migrations/2021_10_02_083144_alter_transaction_booking_id.php deleted file mode 100644 index a37da4e4..00000000 --- a/database/migrations/2021_10_02_083144_alter_transaction_booking_id.php +++ /dev/null @@ -1,45 +0,0 @@ -morphs('owner'); - }); - - //In-case the model name lengthy - Schema::table('transactions', function (Blueprint $table) { - $table->string('owner_type', 250)->change(); - }); - - DB::statement("UPDATE transactions SET owner_type='App\\\\Models\\\\Booking', owner_id = booking_id"); - - Schema::table('transactions', function (Blueprint $table) { - $table->dropForeign('transactions_booking_id_foreign'); - $table->dropColumn('booking_id'); - }); - } - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - // - } -} diff --git a/database/migrations/2020_12_02_131122_create_receipts_table.php b/database/migrations/2022_03_20_170628_create_groups_table.php similarity index 57% rename from database/migrations/2020_12_02_131122_create_receipts_table.php rename to database/migrations/2022_03_20_170628_create_groups_table.php index 16ec2684..569627c2 100644 --- a/database/migrations/2020_12_02_131122_create_receipts_table.php +++ b/database/migrations/2022_03_20_170628_create_groups_table.php @@ -5,7 +5,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class CreateReceiptsTable extends Migration +class CreateGroupsTable extends Migration { /** * Run the migrations. @@ -14,10 +14,11 @@ class CreateReceiptsTable extends Migration */ public function up() { - Schema::create('receipts', function (Blueprint $table) { + Schema::create('groups', function (Blueprint $table) { $table->id(); - $table->foreignId('transaction_id')->unsigned(); - $table->string('bill_no')->unique(); + $table->string('reference')->unique(); + $table->foreignId('issuer')->unsigned(); + $table->foreignId('receiver')->unsigned(); $table->decimal('amount', 14, 5)->default(0.00); $table->decimal('original_amount', 14, 5)->default(0.00); $table->foreignId('currency_id')->unsigned(); @@ -25,17 +26,9 @@ class CreateReceiptsTable extends Migration $table->decimal('currency_rate', 14, 5)->default(0.00); $table->decimal('tax', 14, 5)->default(0.00); $table->decimal('service_charge', 14, 5)->default(0.00); - $table->timestamp('transaction_date')->useCurrent(); - $table->integer('status')->default(ApprovalStatus::COMPLETED); - $table->softDeletes(); + $table->integer('status')->default(ApprovalStatus::PENDING_VERIFICATION); $table->timestamps(); - - $table->foreign('transaction_id')->references('id')->on('transactions'); - $table->foreign('currency_id')->references('id')->on('currencies'); - $table->foreign('original_currency_id')->references('id')->on('currencies'); - }); - } /** @@ -45,6 +38,6 @@ class CreateReceiptsTable extends Migration */ public function down() { - Schema::dropIfExists('receipt'); + Schema::dropIfExists('groups'); } } diff --git a/database/migrations/2021_10_02_082211_drop_wallet_transactions_table.php b/database/migrations/2022_03_20_170641_create_group_transactions_table.php similarity index 51% rename from database/migrations/2021_10_02_082211_drop_wallet_transactions_table.php rename to database/migrations/2022_03_20_170641_create_group_transactions_table.php index f16eeba3..53f33356 100644 --- a/database/migrations/2021_10_02_082211_drop_wallet_transactions_table.php +++ b/database/migrations/2022_03_20_170641_create_group_transactions_table.php @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class DropWalletTransactionsTable extends Migration +class CreateGroupTransactionsTable extends Migration { /** * Run the migrations. @@ -13,7 +13,11 @@ class DropWalletTransactionsTable extends Migration */ public function up() { - Schema::dropIfExists('wallet_transaction'); + Schema::create('group_transactions', function (Blueprint $table) { + $table->id(); + $table->foreignId('group_id')->unsigned(); + $table->foreignId('transaction_id')->unsigned(); + }); } /** @@ -23,6 +27,6 @@ class DropWalletTransactionsTable extends Migration */ public function down() { - // + Schema::dropIfExists('group_transactions'); } } diff --git a/database/migrations/2022_04_03_225513_create_bank_logs_table.php b/database/migrations/2022_04_03_225513_create_bank_logs_table.php new file mode 100644 index 00000000..02fecbd3 --- /dev/null +++ b/database/migrations/2022_04_03_225513_create_bank_logs_table.php @@ -0,0 +1,51 @@ +id(); + $table->foreignId('bank_id')->unsigned(); + $table->foreignId('company_id')->unsigned(); + $table->string('reference')->nullable(); + $table->string('bank_name'); + $table->string('holder_name'); + $table->string('account_no'); + $table->string('bank_branch')->nullable(); + $table->string('swift')->nullable(); + $table->string('snap')->nullable(); + $table->integer('type')->default(BankAccountType::EXTERNAL); + $table->integer('default')->default(false); + $table->integer('status')->default(ApprovalStatus::APPROVED); + $table->foreignId('country_id')->unsigned(); + $table->softDeletes(); + $table->timestamps(); + + $table->foreign('bank_id')->references('id')->on('banks'); + $table->foreign('country_id')->references('id')->on('countries'); + $table->foreign('company_id')->references('id')->on('companies'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('bank_logs'); + } +} diff --git a/database/migrations/2022_04_15_211421_create_notifications_table.php b/database/migrations/2022_04_15_211421_create_notifications_table.php new file mode 100644 index 00000000..d14cdc89 --- /dev/null +++ b/database/migrations/2022_04_15_211421_create_notifications_table.php @@ -0,0 +1,40 @@ +id(); + $table->string('title'); + $table->text('description'); + $table->morphs('subject'); + $table->morphs('target'); + $table->morphs('causer'); + $table->integer('status')->default(ApprovalStatus::APPROVED); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('notifications'); + } +} diff --git a/database/migrations/2023_03_16_223623_seasonal_segment_table.php b/database/migrations/2023_03_16_223623_seasonal_segment_table.php new file mode 100644 index 00000000..4b804086 --- /dev/null +++ b/database/migrations/2023_03_16_223623_seasonal_segment_table.php @@ -0,0 +1,41 @@ +id(); + $table->foreignId('company_id')->unsigned(); + $table->string('status')->default(ApprovalStatus::APPROVED); + $table->foreignId('segment_id')->unsigned(); + $table->timestamp('starting_on')->nullable(); + $table->timestamp('ending_on')->nullable(); + $table->softDeletes(); + $table->timestamps(); + + $table->foreign('segment_id')->references('id')->on('segments'); + $table->foreign('company_id')->references('id')->on('companies'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} 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/database/seeds/CompaniesTableSeeder.php b/database/seeds/CompaniesTableSeeder.php index 5e665a9e..51122432 100644 --- a/database/seeds/CompaniesTableSeeder.php +++ b/database/seeds/CompaniesTableSeeder.php @@ -25,5 +25,15 @@ class CompaniesTableSeeder extends Seeder $company->save(); + $bank = new \App\Models\Bank(); + $bank->company_id = $company->id; + $bank->bank_name = 'Maybank'; + $bank->holder_name = 'CIEF Worldwide Snd Bhd'; + $bank->account_no = '63465345345'; + $bank->country_id = 1; + $bank->status = ApprovalStatus::APPROVED; + $bank->type = \App\Classes\ValueObjects\Constants\BankAccountType::PERSONAL; + $bank->save(); + } } diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index a1928a7a..87d72f71 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -4,6 +4,7 @@ use Database\Seeders\BanksTableDevelopmentSeeder; use Database\Seeders\CompaniesTableDevelopmentSeeder; use Database\Seeders\CurrenciesTableDevelopmentSeeder; use Database\Seeders\CurrencyRatesTableDevelopmentSeeder; +use Database\Seeders\DummyDataSeeder; use Database\Seeders\SegmentConstantsTableDevelopmentSeeder; use Database\Seeders\SegmentsTableDevelopmentSeeder; use Database\Seeders\ServiceTypesTableDevelopmentSeeder; @@ -34,16 +35,17 @@ class DatabaseSeeder extends Seeder $this->call(CompaniesTableSeeder::class); // Admin - $this->call(AdminUserTableSeeder::class); +// $this->call(AdminUserTableSeeder::class); $this->call(AdminUserPermissionsTableSeeder::class); if(App()->environment('local')){ - $this->call(CompaniesTableDevelopmentSeeder::class); - $this->call(BanksTableDevelopmentSeeder::class); +// $this->call(CompaniesTableDevelopmentSeeder::class); +// $this->call(BanksTableDevelopmentSeeder::class); $this->call(ServiceTypesTableDevelopmentSeeder::class); $this->call(SegmentsTableDevelopmentSeeder::class); $this->call(SegmentConstantsTableDevelopmentSeeder::class); $this->call(CurrencyRatesTableDevelopmentSeeder::class); + $this->call(DummyDataSeeder::class); } DB::commit(); diff --git a/database/seeds/DummyDataSeeder.php b/database/seeds/DummyDataSeeder.php new file mode 100644 index 00000000..76378265 --- /dev/null +++ b/database/seeds/DummyDataSeeder.php @@ -0,0 +1,689 @@ +faker = $faker; + $this->createsUser = $createsUser; + $this->createsCompany = $createsCompany; + $this->createsContact = $createsContact; + $this->createsAddress = $createsAddress; + $this->assignEmployeeProcessor = $assignEmployeeProcessor; + $this->createsDocument = $createsDocument; + $this->createsFiles = $createsFiles; + $this->generatesWalletCode = $generatesWalletCode; + $this->createsWallet = $createsWallet; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->createsTransaction = $createsTransaction; + $this->updatesTransactionStatus = $updatesTransactionStatus; + $this->updatesWalletBalance = $updatesWalletBalance; + $this->createsBank = $createsBank; + $this->generatesBookingMarking = $generatesBookingMarking; + $this->createsBooking = $createsBooking; + $this->fetchBookingQuotation = $fetchBookingQuotation; + $this->approvesDocument = $approvesDocument; + $this->rejectsDocument = $rejectsDocument; + $this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor; + $this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor; + $this->createSupplierTransactionProcessor = $createSupplierTransactionProcessor; + $this->assignSegmentProcessor = $assignSegmentProcessor; + $this->setsBankToDefault = $setsBankToDefault; + } + + + /** + * Run the database seeds. + * + * @return void + * @throws AccessForbiddenException + * @throws MalformedRequestException + * @throws RequestValidationException + * @throws MpdfException + */ + public function run() + { + // Local Development Default Password Hash + $password = '123456abcabc'; + + // At the moment we only have 3 different user roles: + // RoleTypes::SUPER_ADMIN : Full access, at the moment is not attached to a company but should be in the future. + // RoleTypes::ADMIN : Full access except for some sensitive features that require higher level of approval, at the moment is not attached to a company but should be in the future. + // RoleTypes::USER : This is the customer, can only access their own orders only, must be attached to a company. + + // User Status + // ApprovalStatus::PENDING_VERIFICATION : This should be the default status before the user verifies their email status, but currently this is not being implemented. + // ApprovalStatus::APPROVED : This is the status of users with verified emails. + // ApprovalStatus::SUSPENDED : This is the status if the users is blocked from the system, but currently this is not being implemented. + + + // =============================================== // + // Create CIEF Entities // + // =============================================== // + + // create super admin + $userObject = new RegistrationObject($this->faker->name, 'super_admin@exchange.com', $password, $password,RoleTypes::SUPER_ADMIN, ApprovalStatus::APPROVED); + $this->createsUser->execute($userObject); + + Auth()->login(User::find(1), true); + + // create admin + $userObject = new RegistrationObject($this->faker->name, 'admin@exchange.com', $password, $password,RoleTypes::ADMIN, ApprovalStatus::APPROVED); + $this->createsUser->execute($userObject); + + // create CIEF + $company_object = new CompanyObject('CIEF Worldwide Sdn Bhd', 'CIEF',CompanyType::COMPANY_BUSINESS,ApprovalStatus::APPROVED); + /** @var Company $company */ + $company = $this->createsCompany->execute($company_object); + + // =============================================== // + // Create Supplier Entities // + // =============================================== // + // supplier entities consist of 2 type of company module [BusinessType::FREIGHT_FORWARDER, BusinessType::FREIGHT_FORWARDER, BusinessType::WAREHOUSE] + // in this use case we are creating 3 supplier, with each supplier having 6 company modules, 1 BusinessType::FREIGHT_FORWARDER and 5 BusinessType::WAREHOUSE. 1 warehouse for each location. + + for ($i = 1; $i <= 3; $i++) { + $supplierName = $this->faker->company; + $supplierReference = $this->faker->bothify('??-????'); + + $company_object = new CompanyObject($supplierName, $supplierReference,BusinessType::CURRENCY_VENDOR, CompanyType::COMPANY_BUSINESS, ApprovalStatus::APPROVED); + /** @var Company $company */ + $company = $this->createsCompany->execute($company_object); + $bank_object = new BankObject($company->id, $this->faker->numberBetween(2, 3), + $this->faker->company, $this->faker->name, $this->faker->bankAccountNumber, + $this->faker->city, null, null, + 2, $this->faker->company); + + $bank = $this->createsBank->execute($bank_object); + $this->setsBankToDefault->execute($bank); + + } + + + // =============================================== // + // Create Customer // + // =============================================== // + // 1. create user + // 2. create company + + // 3. Attach Employee + // 4. create contact + // + // 5. create Address + + // 6. identification verification + + // =============================================== // + // Wallet // + // =============================================== // + + // 7. top up wallet + + // =============================================== // + // Order Workflow // + // =============================================== // + + // 8. create recipient bank + // 9. create booking + // 10. make payment (Manual, FPX, Wallet) + // 11. approve payment (For manual payments only) * N + // 12. create supplier order + // 13. upload china payment proof (outsource * N) + // 14. create purchase order (maybe outsource) + // 15. approve purchase order () + // 16. generate invoice + + // generate random number of users + for($userLoop=1; $userLoop <= 20; $userLoop++) { + + // === // + // 1 // ========== // + // Create user // + // ================= // + $customerName = $this->faker->name; + $customerEmail = $this->faker->email; + $userObject = new RegistrationObject($customerName, $customerEmail, $password, $password, RoleTypes::USER, ApprovalStatus::APPROVED); + /** @var User $user */ + $user = $this->createsUser->execute($userObject); + + // === // + // 2 // ========== // + // Create Company // + // ================= // + + // company reference is called marking, it is the human readable id. + + // CompanyTypes + // CompanyType::COMPANY_BUSINESS : For SME Business Entities and requires SSM for identity verification. + // CompanyType::PERSONAL_BUSINESS : For Personal Entities and requires IC for identity verification, and the company name will follow the customer name in this case. + + // Company Status + // ApprovalStatus::APPROVED : This is the default status of registered company. + // ApprovalStatus::SUSPENDED : This is the status if the company is blocked from releasing packages from warehouse due to pending verification. + + $isCompany = $this->faker->numberBetween(0, 1); + $companyName = $isCompany ? $this->faker->company : $customerName; + + $company_object = new CompanyObject($companyName, + mt_rand(1000, 9999).(new GeneratesInitials())->name($companyName)->length(3)->generate(), + $isCompany ? CompanyType::COMPANY_BUSINESS : CompanyType::PERSONAL_BUSINESS, + ApprovalStatus::APPROVED); + + /** @var Company $company */ + $company = $this->createsCompany->execute($company_object); + $this->assignSegmentProcessor->execute($company); + + + // === // + // 3 // ===========// + // Attach Employee // + // ==================// + // employees are attached to company modules not companies, because an employee maybe working for one or many "Departments". + $Object = new EmploymentObject($company, $user); + $this->assignEmployeeProcessor->execute($Object); + + // === // + // 4 // ========== // + // Create Contact // + // ================= // + // Contacts uses eloquent polymorphic relationship to declare its owner. and for this use case it will be attached to the company not the company module. + $contactObject = new ContactObject($company->id, $customerName, (int) $this->faker->randomNumber(7), $customerEmail, null, 1); + $this->createsContact->execute($contactObject); + + // === // + // 5 // ========== // + // Create Address // + // ================= // + // Addresses uses eloquent polymorphic relationship to declare its owner. and for this use case it will be attached to the company module. + // an address has at least 1 contact for the PIC. + // there is 1 type of address we use: + // AddressType::BILLING : for the invoice billing address + + // create delivery address + $addressObject = new AddressObject($this->faker->streetAddress, '', 1, $this->faker->numberBetween(1, 15), $this->faker->numberBetween(1, 100), $this->faker->postcode); + /** @var Address $address */ + $address = $this->createsAddress->execute($company, $addressObject); + + // === // + // 6 // ====================== // + // identification verification // + // ============================== // + // please refer to company types section for more insight + $object = new DocumentObject($isCompany ? DocumentType::SSM_REGISTRATION : DocumentType::IDENTITY_CARD, ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOtAAANGklEQVR4nOzXDa/fdX3G8R44Ww54BIFV2wFyoxUoKmsFhA0zEGQj1jOMo5o5IQPmYE5wrSvjdhbHAGWt0BWEwmChuHEjSF2LrY6tlmFjJbblprQstD21UFzbrBhX1tKyR3ElJtfr9QCu78k/v5N3PoOzbv/SmKR/mv94dP+FN9dH95+7+J7o/sjse6P7y3d/Orq/6qKTovtLN94f3Z9w39nR/XeFv/+nrlse3V/xhbXR/Xuv/kx0f3TDjuj+oltviO7PHJf9/veJrgPwK0sAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQafOeyu6MPPP9by6L7f7n/yuj+rWN/Et3/xiu/Gd3/xQduju7ve+KE6P7wc6dG9yde/lx0f86J2e/nO38yLrq/ftXT0f0/m7wzuj9jw4vR/V2n74jub7v8zOi+CwCglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKDWw40dD0Qc2PP+O6P6cJauj+zP+dVl0//izT47uv+eazdH9bf+3I7r/xhFXR/dnzjwmun/lJ56N7n/vA/Oi+5NmnxDdf3jqtuj+8j0PRPfnzPtUdP/Jt++K7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSgwsfXhl94KuHrIjuHzvmxuj+g6+9EN1//Pz/jO5P/2x2f/dTL0X3f+e9S6L7f3zJHdH9jafOjO6/7/LDo/s3bL4+un/rnNOi+yMXDUX3Bw6YFt2/+IyTovsuAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACg1OCEe34UfWDn+SPR/bcWrY3u//DMbdH94w4diu4/sPTo6P7tNx0Z3V92wrzo/k8/uzu6f8DwndH9v5i1ILq/+UO3Rfd3Lz4nuv+VRx6K7s9Y+nJ0f+HKsdF9FwBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUGrw9+84JfrAof89P7r/wf8aG90/d8//RvdvXf/30f3JI8ui+8ceeUd0/+DPr4nuf3jX56L7p168Krr/rcfOiu6PHvhqdH/xLauj+8+Mbozur/vpj6P7E8edH913AQCUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQYXb9kafeDSf/lWdP/fdx4c3X9k60vR/b13Xxjdnzb1Z9H90ctWRfffmvRGdP+8e96M7m+avT26/45vnh3d/9rNY6P7Tz/5w+j+e6bMj+4f8rHro/vHH579/l0AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpwb037R994NK5a6P7/7zgvuj+JTNPiO7/7fKjo/t7H7wmuj/8+pnR/f2nbojuH33tndH9333536L7E25+Kro//NuTo/vLT1oe3f+jNZdG9y+ffHB0f972e6L7LgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoNTgYcd8LvrA9IGd0f3jh6+K7l8/d010f5/hrdH98euGovuf+OvbovsHnPpSdP/p6X8e3d8ztD26v+bJydH9GQfcG91/+MWPRve/vPSX0f2P/OmC6P7AdYdF910AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpgXMG3x59YPXKWdH9xc9+P7q/3/SjovvLjjs3uv83E4+N7p904Lej+9MXfCq6//ULvxvdf2D2KdH9afOuiu5PnPm26P7QM1Oy+yPZ7/PItdui+1v+Y1103wUAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQaeOKXH48+sGnNtOj+mGkrovNvm3tddH/MoWOj8y/cckx0/6D5743u/+ydI9H9O/d+JLr/yuwLovtbHhqI7r//wuz3+Z2PfTC6/5VXVkX3/+G+c6L7Xz9vRnTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBp4/ZC7og/84cLx0f1fX7gyuj/xy6PR/eueuDG6P/6rS6L7t//g8ej+Xa9Niu6/9sSM6P4bmx6L7t+14+Do/se/NBzd3/7Rq6L7a976dHR/xruy/1+jO/aN7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSg48/PyX6wBenDkX3Z607Lbo/d9f7o/tblmd//5u+Oyu6f/rP50X3B1/dEN2/5O6V0f0rLr0tur/omp9E918deT26/+Dpe6P7N179d9H9TdeORvd33XdOdN8FAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUGpgzcW/0gQ9PPyK6P/jtB6P7nzxrU3T/i/dvjO6vPuWQ6P6Z7/616P76T94Q3X/37qHo/rzfmx/dv2V8dv/+/S6L7j/6B0uj+wftuyS6v+KK7O+/++Xs3+8CACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKDTx24TeiDzz68MnR/UU7D4/uH/ah7P6PH5oT3T/viCuj+18b2RTd//7J+0f3L7nipuj+lAueie6PmXpUdH7mpDOi+y/ueiS6f9kP/iq6/+yEn0f3p1x0UXTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBo86urN0Qemjk6N7n9v+Nzo/uHXfj66P3fF1uj+Pvv9T3T/wBOj82MeOuvR6P64cVdE9/9x4KDo/m+cdmV0f8HSPdH9910wKbo//gv3Rve/+YszovufufO46L4LAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAo9f8BAAD//3aYjHM9JD/iAAAAAElFTkSuQmCC'], + $isCompany ? $this->faker->bothify('SSM-#######') : $this->faker->bothify('############'), ApprovalStatus::APPROVED, 'identifications'); + /** @var Document $document */ + $document = $this->createsDocument->execute($company, $object); + $this->createsFiles->execute($document, $object); + + // === // + // 7 // ========= // + // top up wallet // + // ================ // + // when a customer tries to top up their wallet, if the wallet doesn't already exist it will be automatically created. + // wallet credit can be used to pay for transfer orders to enjoy better conversion rates. + // wallet top-ups can only be performed using FPX at the moment. but super admin can manually credit or debit credit to a customer's wallet + + // The transaction table is considered the most confusing part of our database because it is being used by multiple model using a polymorphic relationship + // and is used for many use cases in our application which is an unintended flaw, and we are looking for ways to improve it. + + + // A wallet top up is TransactionType::TOP_UP, there are many types of transactions used by a wallet: + // TransactionType::TOP_UP : represent a top-up amount to a wallet; + // TransactionType::PAYMENT : represent payment out of the wallet; + // TransactionType::CREDIT_NOTE : represent a manual top-up to a wallet, and can only be performed by super admin; + // TransactionType::DEBDIT_NOTE : represent a deduction from a wallet, and can only be performed by super admin; + // TransactionType::WITHDRAW : represent a customer withdrawing credit out of a wallet to a bank account (refund); + + + // top up only some customers + $shouldTopUp = $this->faker->numberBetween(0, 1); + if($shouldTopUp) { + $object = new WalletObject($company->id, 1, $this->generatesWalletCode->execute()); + /** @var Wallet $wallet */ + $wallet = $this->createsWallet->execute($object, $company); + + $amount = $this->faker->numberBetween(10, 300000); + $billNumber = $this->generatesTransactionBillNumber->execute('TOPUP-'); + + // create billplz bill using api, we will skip this part in the seed. + $billPlzBill = $this->faker->bothify('???#####'); + + $transaction_object = new TransactionObject($billNumber, TransactionType::TOP_UP, 1, $company->id, 1, PaymentMethodType::PAYMENT_GATEWAY, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, [], $billPlzBill); + + /** @var Transaction $transaction */ + $transaction = $this->createsTransaction->execute($wallet, $transaction_object); + + + // on billplz callback url + $status = $this->faker->randomElement([ApprovalStatus::APPROVED, ApprovalStatus::REJECTED]); + $this->updatesTransactionStatus->execute($transaction, $status); + if($status === ApprovalStatus::APPROVED) { + $this->updatesWalletBalance->execute($wallet, $amount); + } + } + + // === // + // 8 // ========================== // + // Create Recipient Bank Accounts // + // ================================= // + // bank accounts are used to store bank account details, and can be used in a variety of ways + // Bank types: + // 1. PERSONAL : belong to the same entity + // 2. EXTERNAL : Doesn't belong to the entity, belongs to an external entity; + // 3. ALIPAY : : Is an external entity, but flag the type of bank as alipay e-wallet; + // + // here are some of the current use cases for banks in our application: + // 1. Recipient bank (EXTERNAL) (the account the customer is requesting to transfer funds to) + // 2. AliPay Transfer (EXTERNAL) (the account the customer is requesting to transfer funds to when bank type is ALIPAY) + // 3. Refund bank (PERSONAL) (the account the customer is requesting his order refunds to be transferred to) + $bank_object = new BankObject($company->id, $this->faker->numberBetween(2, 3), + $this->faker->company, $this->faker->name, $this->faker->bankAccountNumber, + $this->faker->city, null, null, + 2, $this->faker->company); + + // todo create multiple bank accounts with multiple types + $bank = $this->createsBank->execute($bank_object); + + // generate random number of bookings + for($orderLoop=1; $orderLoop <= rand(1, 5); $orderLoop++) { + echo 'booking created'; + // === // + // 9 // ========= // + // Create Booking // + // ================ // + // A booking is simply a transfer order to a supplier/manufacturer bank account overseas + // to pay for goods they are buying from overseas. the booking is not proceed until the + // customer requests to make a payment, when the customer start the payment process he + // will receive a quote for the cost to transfer the booked amount (e.g. 100 USD) in RM + + // bookings require 2 actions from the customer to be completed + // 1. Make Full payment ** + // 2. Provide Purchase Order (itemized list of the products they are buying) + + // ** A booking will be the sum of payments transferred to one bank account + // but can be partially paid (e.g. 1000 USD can be paid: $300 deposit + $700 balance) + // A shipping label can be re-used, and each batch that arrives at the supplier warehouse + // is referred to as a packing list. more on this later. + + // service types are configured by the super admin from the settings + // it will include things like conversion rates, service charge, etc.. + // and can be used to place different type of transfer orders (e.g. 1 day transfer, 3 days transfer, 1688 Payment) + + // randomly selects a service type + $service = ServiceType::inRandomOrder()->first(); + + // Booking human readable id + $reference = $this->generatesBookingMarking->execute(); + + // random currency booking (CNY, USD) + $bookedCurrency = 2; + $bank = $company->banks()->inRandomOrder()->first(); + $object = new BookingObject($service->id, $bank->id, $reference, $this->faker->numberBetween(10, 300000), $bookedCurrency, $bookedCurrency, 1); + $booking = $this->createsBooking->execute($company, $object); + + + // === // + // 10 // ====== // + // make payment // + // ============== // + // There are few type of transactions related to a booking: + // TransactionType::PAYMENT : is used for 2 type of use cases (1. payments to transfer orders, 2. payment out of wallet) and is attached to a booking; + // TransactionType::BILL : is to represent the payment out to CIEF currency supplier (expenses) and is attached to a transaction of type TransactionType::PAYMENT; + // TransactionType::TRANSFER_FEE : is to represent the transfer fee charged by CIEF currency supplier is attached to a transaction type TransactionType::BILL; + // TransactionType::REFUND : is to represent a request for refund on a payment, and is attached to a transaction type TransactionType::PAYMENT; + + $numberOfPayments = $this->faker->numberBetween(0, 3); + + for ($paymentLoop=0; $paymentLoop <= $numberOfPayments; $paymentLoop++) { + echo 'payment created'; + $shouldSubmit = $this->faker->numberBetween(0, 1); + $shouldApprove = $this->faker->numberBetween(0, 1); + if ($numberOfPayments > 1){ + + $amount = $booking->fix_amount / $numberOfPayments; + + $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $amount)), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS['cash']); + + $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject); + + $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(10), ApprovalStatus::PENDING_SUBMISSION, [], null); + + /** @var Transaction $transaction */ + $transaction = $this->createsTransaction->execute($booking, $object); + + if($shouldSubmit || $shouldApprove) { + $object = new DocumentObject( DocumentType::CUSTOMER_PAYMENT_PROOF, ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOtAAANGklEQVR4nOzXDa/fdX3G8R44Ww54BIFV2wFyoxUoKmsFhA0zEGQj1jOMo5o5IQPmYE5wrSvjdhbHAGWt0BWEwmChuHEjSF2LrY6tlmFjJbblprQstD21UFzbrBhX1tKyR3ElJtfr9QCu78k/v5N3PoOzbv/SmKR/mv94dP+FN9dH95+7+J7o/sjse6P7y3d/Orq/6qKTovtLN94f3Z9w39nR/XeFv/+nrlse3V/xhbXR/Xuv/kx0f3TDjuj+oltviO7PHJf9/veJrgPwK0sAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQafOeyu6MPPP9by6L7f7n/yuj+rWN/Et3/xiu/Gd3/xQduju7ve+KE6P7wc6dG9yde/lx0f86J2e/nO38yLrq/ftXT0f0/m7wzuj9jw4vR/V2n74jub7v8zOi+CwCglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKDWw40dD0Qc2PP+O6P6cJauj+zP+dVl0//izT47uv+eazdH9bf+3I7r/xhFXR/dnzjwmun/lJ56N7n/vA/Oi+5NmnxDdf3jqtuj+8j0PRPfnzPtUdP/Jt++K7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSgwsfXhl94KuHrIjuHzvmxuj+g6+9EN1//Pz/jO5P/2x2f/dTL0X3f+e9S6L7f3zJHdH9jafOjO6/7/LDo/s3bL4+un/rnNOi+yMXDUX3Bw6YFt2/+IyTovsuAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACg1OCEe34UfWDn+SPR/bcWrY3u//DMbdH94w4diu4/sPTo6P7tNx0Z3V92wrzo/k8/uzu6f8DwndH9v5i1ILq/+UO3Rfd3Lz4nuv+VRx6K7s9Y+nJ0f+HKsdF9FwBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUGrw9+84JfrAof89P7r/wf8aG90/d8//RvdvXf/30f3JI8ui+8ceeUd0/+DPr4nuf3jX56L7p168Krr/rcfOiu6PHvhqdH/xLauj+8+Mbozur/vpj6P7E8edH913AQCUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQYXb9kafeDSf/lWdP/fdx4c3X9k60vR/b13Xxjdnzb1Z9H90ctWRfffmvRGdP+8e96M7m+avT26/45vnh3d/9rNY6P7Tz/5w+j+e6bMj+4f8rHro/vHH579/l0AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpwb037R994NK5a6P7/7zgvuj+JTNPiO7/7fKjo/t7H7wmuj/8+pnR/f2nbojuH33tndH9333536L7E25+Kro//NuTo/vLT1oe3f+jNZdG9y+ffHB0f972e6L7LgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoNTgYcd8LvrA9IGd0f3jh6+K7l8/d010f5/hrdH98euGovuf+OvbovsHnPpSdP/p6X8e3d8ztD26v+bJydH9GQfcG91/+MWPRve/vPSX0f2P/OmC6P7AdYdF910AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpgXMG3x59YPXKWdH9xc9+P7q/3/SjovvLjjs3uv83E4+N7p904Lej+9MXfCq6//ULvxvdf2D2KdH9afOuiu5PnPm26P7QM1Oy+yPZ7/PItdui+1v+Y1103wUAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQaeOKXH48+sGnNtOj+mGkrovNvm3tddH/MoWOj8y/cckx0/6D5743u/+ydI9H9O/d+JLr/yuwLovtbHhqI7r//wuz3+Z2PfTC6/5VXVkX3/+G+c6L7Xz9vRnTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBp4/ZC7og/84cLx0f1fX7gyuj/xy6PR/eueuDG6P/6rS6L7t//g8ej+Xa9Niu6/9sSM6P4bmx6L7t+14+Do/se/NBzd3/7Rq6L7a976dHR/xruy/1+jO/aN7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSg48/PyX6wBenDkX3Z607Lbo/d9f7o/tblmd//5u+Oyu6f/rP50X3B1/dEN2/5O6V0f0rLr0tur/omp9E918deT26/+Dpe6P7N179d9H9TdeORvd33XdOdN8FAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUGpgzcW/0gQ9PPyK6P/jtB6P7nzxrU3T/i/dvjO6vPuWQ6P6Z7/616P76T94Q3X/37qHo/rzfmx/dv2V8dv/+/S6L7j/6B0uj+wftuyS6v+KK7O+/++Xs3+8CACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKDTx24TeiDzz68MnR/UU7D4/uH/ah7P6PH5oT3T/viCuj+18b2RTd//7J+0f3L7nipuj+lAueie6PmXpUdH7mpDOi+y/ueiS6f9kP/iq6/+yEn0f3p1x0UXTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBo86urN0Qemjk6N7n9v+Nzo/uHXfj66P3fF1uj+Pvv9T3T/wBOj82MeOuvR6P64cVdE9/9x4KDo/m+cdmV0f8HSPdH9910wKbo//gv3Rve/+YszovufufO46L4LAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAo9f8BAAD//3aYjHM9JD/iAAAAAElFTkSuQmCC'], + '', ApprovalStatus::PENDING_VERIFICATION, 'payments'); + /** @var Document $document */ + $document = $this->createsDocument->execute($transaction, $object); + $this->createsFiles->execute($document, $object); + $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::PENDING_SUBMISSION); + } + + if($shouldApprove) { + $this->approvesDocument->execute($document); + $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED); + + $shouldReject = $this->faker->numberBetween(0, 1); + if($shouldReject){ + $this->rejectsDocument->execute($document); + $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::REJECTED); + } + } + } + + } + + // creating the purchase order can happen before or after the payment is made, the customer needs to fill up the list of product + // they are buying and attaching it to the booking, a purchase order is a transaction of type TransactionType::PURCHASE_ORDER + $billNumber = $this->generatesTransactionBillNumber->execute('PO-'); + + + $shouldSubmit = $this->faker->numberBetween(0, 1); + $shouldApprove = $this->faker->numberBetween(0, 1); + + if($shouldSubmit){ + $completeSubmission = $this->faker->numberBetween(0, 1); + + $quantity = $this->faker->numberBetween(5, 200); + $unitPrice = $booking->fix_amount / $quantity; + + $products = collect([[ + 'stockCode' => $this->faker->numerify('#####'), + 'description' => $this->faker->text, + 'quantity' => $completeSubmission ? $quantity : $quantity - $this->faker->numberBetween(1, 4), + 'unit_price' => (string) round($unitPrice, 5) + ]]); + + $total = $products->first()['quantity'] * (float) $products->first()['unit_price']; + + $object = new TransactionObject($billNumber, TransactionType::PURCHASE_ORDER, $booking->company->id, 1, + 1, PaymentMethodType::CASH, + $total, $total, $booking->fix_currency_id, $booking->fix_currency_id, + 1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, $products->toArray()); + + $transaction = $this->createPurchaseOrderTransactionProcessor->execute($booking, $object); + } + + if($transaction->status === ApprovalStatus::PENDING_VERIFICATION) { + $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED); + } + + + + } + + // when processing a customer order, we will place an order with one of our currency supplier which will generate a transaction type TransactionType::BILL + // and attach it to the customer payment TransactionType::PAYMENT, and it will update the TransactionType::PAYMENT status to ApprovalStatus::COMPLETED + $totalApprovedPayments = Transaction::where('type', TransactionType::PAYMENT)->where('status', 2)->count(); + $totalWhiteForms = round($totalApprovedPayments / $this->faker->numberBetween(2, 5)); + $perWhiteForm = $totalApprovedPayments / (round($totalWhiteForms / 2) ?: 1); + + for($orderLoop=1; $orderLoop <= ($totalWhiteForms / 2); $orderLoop++) { + $supplier = Company::where('business_type', BusinessType::CURRENCY_VENDOR)->inRandomOrder()->first(); + $rate = $this->faker->randomFloat(5, 1.3, 1.6); + $payments = Transaction::where('type', TransactionType::PAYMENT)->where('status', ApprovalStatus::APPROVED)->inRandomOrder()->limit($perWhiteForm)->get(); + + $this->createSupplierTransactionProcessor->execute($supplier, $rate, $payments->toArray()); + + $group = new Group(); + $group->save(); + + $issuer = ''; + $receiver = ''; + $amount = 0; + $original_amount = 0; + $currency_id = 0; + $original_currency_id = ''; + $currency_rate = ''; + $tax = 0; + $service_charge = 0; + + foreach ($this->createSupplierTransactionProcessor->getBills() as $key => $row) { + $group->transactions()->sync($row->id, false); + $issuer = $row->issuer; + $receiver = $row->receiver; + $amount += $row->amount; + $original_amount += $row->original_amount; + $currency_id = $row->currency_id; + $original_currency_id = $row->original_currency_id; + $currency_rate = $row->currency_rate; + $tax += $row->tax; + $service_charge += $row->service_charge; + } + + $group->issuer = $issuer; + $group->receiver = $receiver; + $group->reference = $this->generatesTransactionBillNumber->execute('SPO-'); + $group->amount = $amount; + $group->original_amount = $original_amount; + $group->currency_id = $currency_id; + $group->original_currency_id = $original_currency_id; + $group->currency_rate = $currency_rate; + $group->tax = $tax; + $group->service_charge = $service_charge; + + $group->update(); + + $pdf = LaravelMpdf::loadView('pages.pdfs.currency_vendor_order', ['transactions' => $this->createSupplierTransactionProcessor->getBills(), 'transferFeeTransactions' => $this->createSupplierTransactionProcessor->getTransferTransactions(), 'supplier' => $supplier]); + + $object = new DocumentObject( + DocumentType::CURRENCY_VENDOR_ORDER, + [chunk_split('data:application/pdf;base64,'.base64_encode($pdf->output()))], + '', + ApprovalStatus::COMPLETED, + 'currency_vendor_order' + ); + + /** @var Document $document */ + $document = $this->createsDocument->execute($group, $object); + $this->createsFiles->execute($document, $object); + + + foreach ($payments as $payment) { + $chinaBankSlipUploaded = $this->faker->numberBetween(0, 1); + + if($chinaBankSlipUploaded){ + $bill = $payment->transactions()->where('type', TransactionType::BILL)->first(); + + // when our currency supplier completes the transfer they will send us the bank slip as proof of payment, then the admin user + // will upload the bank slip document and attaching it to transaction type TransactionType::BILL + $object = new DocumentObject( DocumentType::CUSTOMER_PAYMENT_PROOF, ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOtAAANGklEQVR4nOzXDa/fdX3G8R44Ww54BIFV2wFyoxUoKmsFhA0zEGQj1jOMo5o5IQPmYE5wrSvjdhbHAGWt0BWEwmChuHEjSF2LrY6tlmFjJbblprQstD21UFzbrBhX1tKyR3ElJtfr9QCu78k/v5N3PoOzbv/SmKR/mv94dP+FN9dH95+7+J7o/sjse6P7y3d/Orq/6qKTovtLN94f3Z9w39nR/XeFv/+nrlse3V/xhbXR/Xuv/kx0f3TDjuj+oltviO7PHJf9/veJrgPwK0sAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQafOeyu6MPPP9by6L7f7n/yuj+rWN/Et3/xiu/Gd3/xQduju7ve+KE6P7wc6dG9yde/lx0f86J2e/nO38yLrq/ftXT0f0/m7wzuj9jw4vR/V2n74jub7v8zOi+CwCglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKDWw40dD0Qc2PP+O6P6cJauj+zP+dVl0//izT47uv+eazdH9bf+3I7r/xhFXR/dnzjwmun/lJ56N7n/vA/Oi+5NmnxDdf3jqtuj+8j0PRPfnzPtUdP/Jt++K7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSgwsfXhl94KuHrIjuHzvmxuj+g6+9EN1//Pz/jO5P/2x2f/dTL0X3f+e9S6L7f3zJHdH9jafOjO6/7/LDo/s3bL4+un/rnNOi+yMXDUX3Bw6YFt2/+IyTovsuAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACg1OCEe34UfWDn+SPR/bcWrY3u//DMbdH94w4diu4/sPTo6P7tNx0Z3V92wrzo/k8/uzu6f8DwndH9v5i1ILq/+UO3Rfd3Lz4nuv+VRx6K7s9Y+nJ0f+HKsdF9FwBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUGrw9+84JfrAof89P7r/wf8aG90/d8//RvdvXf/30f3JI8ui+8ceeUd0/+DPr4nuf3jX56L7p168Krr/rcfOiu6PHvhqdH/xLauj+8+Mbozur/vpj6P7E8edH913AQCUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQYXb9kafeDSf/lWdP/fdx4c3X9k60vR/b13Xxjdnzb1Z9H90ctWRfffmvRGdP+8e96M7m+avT26/45vnh3d/9rNY6P7Tz/5w+j+e6bMj+4f8rHro/vHH579/l0AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpwb037R994NK5a6P7/7zgvuj+JTNPiO7/7fKjo/t7H7wmuj/8+pnR/f2nbojuH33tndH9333536L7E25+Kro//NuTo/vLT1oe3f+jNZdG9y+ffHB0f972e6L7LgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoNTgYcd8LvrA9IGd0f3jh6+K7l8/d010f5/hrdH98euGovuf+OvbovsHnPpSdP/p6X8e3d8ztD26v+bJydH9GQfcG91/+MWPRve/vPSX0f2P/OmC6P7AdYdF910AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpgXMG3x59YPXKWdH9xc9+P7q/3/SjovvLjjs3uv83E4+N7p904Lej+9MXfCq6//ULvxvdf2D2KdH9afOuiu5PnPm26P7QM1Oy+yPZ7/PItdui+1v+Y1103wUAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQaeOKXH48+sGnNtOj+mGkrovNvm3tddH/MoWOj8y/cckx0/6D5743u/+ydI9H9O/d+JLr/yuwLovtbHhqI7r//wuz3+Z2PfTC6/5VXVkX3/+G+c6L7Xz9vRnTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBp4/ZC7og/84cLx0f1fX7gyuj/xy6PR/eueuDG6P/6rS6L7t//g8ej+Xa9Niu6/9sSM6P4bmx6L7t+14+Do/se/NBzd3/7Rq6L7a976dHR/xruy/1+jO/aN7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSg48/PyX6wBenDkX3Z607Lbo/d9f7o/tblmd//5u+Oyu6f/rP50X3B1/dEN2/5O6V0f0rLr0tur/omp9E918deT26/+Dpe6P7N179d9H9TdeORvd33XdOdN8FAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUGpgzcW/0gQ9PPyK6P/jtB6P7nzxrU3T/i/dvjO6vPuWQ6P6Z7/616P76T94Q3X/37qHo/rzfmx/dv2V8dv/+/S6L7j/6B0uj+wftuyS6v+KK7O+/++Xs3+8CACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKDTx24TeiDzz68MnR/UU7D4/uH/ah7P6PH5oT3T/viCuj+18b2RTd//7J+0f3L7nipuj+lAueie6PmXpUdH7mpDOi+y/ueiS6f9kP/iq6/+yEn0f3p1x0UXTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBo86urN0Qemjk6N7n9v+Nzo/uHXfj66P3fF1uj+Pvv9T3T/wBOj82MeOuvR6P64cVdE9/9x4KDo/m+cdmV0f8HSPdH9910wKbo//gv3Rve/+YszovufufO46L4LAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAo9f8BAAD//3aYjHM9JD/iAAAAAElFTkSuQmCC'], + '', ApprovalStatus::APPROVED, 'china_bank_slip'); + /** @var Document $document */ + $document = $this->createsDocument->execute($bill, $object); + $this->createsFiles->execute($document, $object); + + $this->updatesTransactionStatus->execute($bill, ApprovalStatus::APPROVED); + + // the invoicing documents will be generated once they 2 conditions are met: + // 1. Full payment completed (completed is flagged when the china payment proof is uploaded) + // 2. The purchase order is filled and approved (when the purchase order is not filled for more than 2 months the system will automatically generate a random products for Purchase order to close the order) + + // once the invoice is generated the transaction table will include 2 new transaction type TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVERY + // and for documents will be generated and attached to the booking. + // once this process is complete the booking status will update to ApprovalStatus::COMPLETED + $this->createInvoiceTransactionProcessor->execute($booking); + + } + } + + + } + } + + } + +} diff --git a/database/seeds/RecoverGroupTransactionTableSeeder.php b/database/seeds/RecoverGroupTransactionTableSeeder.php new file mode 100644 index 00000000..02edb6a1 --- /dev/null +++ b/database/seeds/RecoverGroupTransactionTableSeeder.php @@ -0,0 +1,116 @@ +createsDocument = $createsDocument; + $this->createsFile = $createsFile; + $this->generatesGroupTransactionBillNumber = $generatesGroupTransactionBillNumber; + } + + + /** + * Run the database seeds. + * + */ + public function run() + { + DB::beginTransaction(); + + $transaction_group = Transaction:: + select('issuer', 'currency_rate', 'type', DB::raw('count(DISTINCT id) as total'), DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d %H:%i') as new_date")) + ->where('type', 3) + ->groupBy( + 'issuer', + 'currency_rate', + 'new_date' + ) + ->orderBy('id')->get(); + + foreach ($transaction_group as $group) { + $transactions = Transaction:: + where('type', 3) + ->where('issuer', $group->issuer) + ->where('currency_rate', $group->currency_rate) + ->where(DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d %H:%i')"), $group->new_date) + ->get(); + + $group = new Group(); + $group->save(); + + $issuer = ''; + $date = now(); + $receiver = ''; + $amount = 0; + $original_amount = 0; + $currency_id = 0; + $original_currency_id = ''; + $currency_rate = ''; + $tax = 0; + $service_charge = 0; + + foreach ($transactions as $transaction) { + $group->transactions()->sync($transaction->id, false); + $issuer = $transaction->issuer; + $date = $transaction->created_at; + $receiver = $transaction->receiver; + $amount += $transaction->amount; + $original_amount += $transaction->original_amount; + $currency_id = $transaction->currency_id; + $original_currency_id = $transaction->original_currency_id; + $currency_rate = $transaction->currency_rate; + $tax += $transaction->tax; + $service_charge += $transaction->service_charge; + } + + $group->issuer = $issuer; + $group->receiver = $receiver; + $group->reference = $this->generatesGroupTransactionBillNumber->execute('SPO-', $date); + $group->amount = $amount; + $group->original_amount = $original_amount; + $group->currency_id = $currency_id; + $group->original_currency_id = $original_currency_id; + $group->currency_rate = $currency_rate; + $group->tax = $tax; + $group->service_charge = $service_charge; + $group->status = ApprovalStatus::PENDING_SUBMISSION; + $group->created_at = $date; + $group->updated_at = $date; + + $group->update(); + } + + DB::commit(); + + GenerateGroupTransactionsWhiteForm::dispatch(); + } +} diff --git a/database/seeds/UpdateSuppliersReferenceSeeder.php b/database/seeds/UpdateSuppliersReferenceSeeder.php new file mode 100644 index 00000000..3764664a --- /dev/null +++ b/database/seeds/UpdateSuppliersReferenceSeeder.php @@ -0,0 +1,42 @@ +get(); + + foreach ($suppliers as $supplier) { + if(in_array($supplier->name, ['ATVANTIC IMPORT EXPORT SDN BHD', 'Atvantic - JACK'])) $supplier->reference = 'ATVANTIC IMPORT EXPORT SDN BHD (1309816-P)'; + if(in_array($supplier->name, ['BK GEMILANG SDN BHD', 'BK GEMILANG - JACK'])) $supplier->reference = 'BK GEMILANG SDN BHD (1403513-U)'; + if(in_array($supplier->name, ['YSN - Teh', 'YSN Solution Trading Sdn Bhd - Teh', 'YSN Solution Trading Sdn Bhd', 'YSN SOLUTION TRADING SDN BHD - Jack'])) $supplier->reference = 'YSN Solution Trading Sdn Bhd (1393892-D)'; + if(in_array($supplier->name, ['RACK SOLUTION INDUSTRIES SDN BHD'])) $supplier->reference = 'RACK SOLUTION INDUSTRIES SDN BHD (954723-W)'; + if(in_array($supplier->name, ['RACK SOLUTION INDUSTRIES SDN BHD'])) $supplier->reference = 'RACK SOLUTION INDUSTRIES SDN BHD (954723-W)'; + if(in_array($supplier->name, ['OFY UNION SDN BHD'])) $supplier->reference = 'OFY UNION SDN BHD (1410695-H)'; + if(in_array($supplier->name, ['Simply Infantry Sdn. Bhd.', 'SIMPLY INFANTRY SDN. BHD. - JACK'])) $supplier->reference = 'Simply Infantry Sdn. Bhd. (14393131-W)'; + if(in_array($supplier->name, ['HIGH HILL INTERNATIONAL MARKETING SDN BHD', 'HIGH HILL INTERNATIONAL SDN BHD - JACK'])) $supplier->reference = 'HIGH HILL INTERNATIONAL MARKETING SDN BHD (1419836-X)'; + if(in_array($supplier->name, ['CNT CARGO SDN BHD', 'CNT CARGO SDN BHD - JACK'])) $supplier->reference = 'CNT CARGO SDN BHD 202101036178(1436478-V)'; + if(in_array($supplier->name, ['WEST EXPRESS INTERNATIONAL TRADING SDN BHD'])) $supplier->reference = 'WEST EXPRESS INTERNATIONAL TRADING SDN BHD (1432178-T)'; + if(in_array($supplier->name, ['CIEF WORLDWIDE SDN. BHD.'])) $supplier->reference = 'CIEF WORLDWIDE SDN. BHD. (1134596-M)'; + + $supplier->save(); + } + + DB::commit(); + } +} diff --git a/docker-setup/Dockerfile b/docker-setup/Dockerfile new file mode 100644 index 00000000..71255f30 --- /dev/null +++ b/docker-setup/Dockerfile @@ -0,0 +1,41 @@ +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 \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libzip-dev \ + zip \ + cron \ + supervisor \ + nano \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j$(nproc) gd \ + && docker-php-ext-install zip \ + && docker-php-ext-install bcmath + +COPY --from=composer:1.9.3 /usr/bin/composer /usr/bin/composer + +#NODEJS & NPM +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 + +# 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 new file mode 100644 index 00000000..4dcfa8c2 --- /dev/null +++ b/docker-setup/docker-compose.yml @@ -0,0 +1,53 @@ +version: '3' + +networks: + exchange-staging: + +services: + ################################################################# + nginx: + image: nginx:stable-alpine + container_name: exchange-ngnix + ports: + - "8082:80" + volumes: + - ../:/var/www/html + - ./nginx/default.conf:/etc/nginx/conf.d/default.conf + depends_on: + - php + - mysql + networks: + - exchange-staging + ################################################################# + mysql: + image: mysql:5.7.29 + container_name: exchange-mysql + restart: unless-stopped + tty: true + ports: + - 3302:3306 + environment: + MYSQL_ROOT_USER: root + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: exchange-db + MYSQL_USER: master + MYSQL_PASSWORD: cDe7gcrRBWetaAP + volumes: + - mysql-data:/var/lib/mysql + networks: + - exchange-staging + ################################################################# + php: + build: + context: . + dockerfile: Dockerfile + container_name: exchange-php + volumes: + - ../:/var/www/html + - ./php/default.conf:/usr/local/etc/php-fpm.d/zz-docker.conf + networks: + - exchange-staging + ################################################################# + +volumes: + mysql-data: diff --git a/docker-setup/nginx/default.conf b/docker-setup/nginx/default.conf new file mode 100644 index 00000000..c1811261 --- /dev/null +++ b/docker-setup/nginx/default.conf @@ -0,0 +1,38 @@ +server { + listen 80; + index index.php index.html; + server_name localhost; + error_log /var/log/nginx/error.log; + access_log /var/log/nginx/access.log; + root /var/www/html/public; + + server_name localhost; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass php:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + 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; + fastcgi_read_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/package.json b/package.json index ced333fa..5d4b92bf 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ }, "devDependencies": { "axios": "^0.21.0", + "chromatic": "^6.5.4", "cross-env": "^7.0.2", "del": "^6.0.0", "fancy-log": "^1.3.0", diff --git a/resources/assets/images/1688_approved.png b/resources/assets/images/1688_approved.png new file mode 100644 index 00000000..4275de56 Binary files /dev/null and b/resources/assets/images/1688_approved.png differ diff --git a/resources/assets/images/1688_logo.png b/resources/assets/images/1688_logo.png new file mode 100644 index 00000000..dbcc92b1 Binary files /dev/null and b/resources/assets/images/1688_logo.png differ diff --git a/resources/assets/images/best-rate-300x127.png b/resources/assets/images/best-rate-300x127.png new file mode 100644 index 00000000..c5e9b1c8 Binary files /dev/null and b/resources/assets/images/best-rate-300x127.png differ diff --git a/resources/assets/images/favicon/site.webmanifest b/resources/assets/images/favicon/site.webmanifest index 45dc8a20..97135d16 100644 --- a/resources/assets/images/favicon/site.webmanifest +++ b/resources/assets/images/favicon/site.webmanifest @@ -1 +1 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file +{"name":"","short_name":"","icons":[{"src":"images/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"images/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/resources/assets/sass/modules/_buttons.scss b/resources/assets/sass/modules/_buttons.scss index 3a9879eb..e7b281f8 100644 --- a/resources/assets/sass/modules/_buttons.scss +++ b/resources/assets/sass/modules/_buttons.scss @@ -123,7 +123,11 @@ button:focus{ button:disabled { cursor: not-allowed; -} +} + +.not-allowed { + cursor: not-allowed; +} /* Alternate buttons -------------------------------------------------- diff --git a/resources/assets/sass/modules/_typography.scss b/resources/assets/sass/modules/_typography.scss index 605b8e7c..aa4506a9 100644 --- a/resources/assets/sass/modules/_typography.scss +++ b/resources/assets/sass/modules/_typography.scss @@ -316,6 +316,12 @@ hr{ background-color: $color-primary-lighter !important; } +.bg-primary-lighter-hover { + &:hover { + background-color: $color-primary-lighter !important; + } +} + /* Complete ------------------------------------ */ diff --git a/resources/assets/vue/app.js b/resources/assets/vue/app.js index 1d98d12d..abb7a17e 100644 --- a/resources/assets/vue/app.js +++ b/resources/assets/vue/app.js @@ -24,6 +24,8 @@ import crudHandler from './general/mixins/crudHandler'; /** Directives */ import closable from './general/directives/closable'; +import { debounce } from 'vue-debounce' + import Avatar from 'vue-avatar'; /** Application Injections */ @@ -37,7 +39,7 @@ Vue.mixin({ route: route }, mixins: [request, crudHandler] - }); +}); /** Components Registrations */ Vue.component(Avatar); @@ -48,11 +50,77 @@ files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files( const app = new Vue({ el: '#app', store, + data(){ + return { + captchaId: '', + reCaptchaToken: null + } + }, created(){ + this.init(); this.routesGuard(); + if (!this.$store.getters.isAdmin && !('company_marking' in this.$store.getters.getDecodedAccessToken.user) && this.isProtectedRoute() && !this.isWithTokenRoute()) { + this.$store.dispatch('crudRequest', {endpoint: this.route('api.account.authentication.refresh'), method: 'get'}).then(response => { + let success = response.ok; + response.json().then(response => { + + if(!success){return;} + + this.$store.dispatch('userAuthentication', {access_token: response.payload.refresh_token, redirect_url: window.location.href}); + + }); + }) + + } }, methods: { - route: route + route: route, + init() { + if (!document.getElementById('gRecaptchaScript')) { + + window.gRecaptchaOnLoadCallbacks = [this.render]; + window.gRecaptchaOnLoad = function () { + for (let i = 0; i < window.gRecaptchaOnLoadCallbacks.length; i++) { + window.gRecaptchaOnLoadCallbacks[i](); + } + delete window.gRecaptchaOnLoadCallbacks; + delete window.gRecaptchaOnLoad; + }; + + let recaptchaScript = document.createElement('script'); + recaptchaScript.setAttribute('src', 'https://www.google.com/recaptcha/api.js?render=explicit&onload=gRecaptchaOnLoad'); + recaptchaScript.setAttribute('id', 'gRecaptchaScript'); + recaptchaScript.async = true; + recaptchaScript.defer = true; + document.head.appendChild(recaptchaScript); + + } else if (!window.grecaptcha || !window.grecaptcha.render) { + window.gRecaptchaOnLoadCallbacks.push(this.render); + } else { + this.render(); + } + }, + render() { + this.captchaId = window.grecaptcha.render('grecaptcha_container', { + sitekey: '6Lft5mkhAAAAAAFgQ0gWlwte1h-o6UPRpMNHP1xz', + badge: '', + size: 'invisible', + 'expired-callback': this.execute + }); + this.execute(); + }, + execute: debounce(function() { + this.updateCaptchaToken(); + }, 3000), + updateCaptchaToken() { + if(this.$store.getters.getReCaptcha === this.reCaptchaToken){ + window.grecaptcha.execute(this.captchaId).then((token) => { + this.reCaptchaToken = token; + this.$store.dispatch('reCaptcha', {token: token}); + }); + } + + } }, mixins: [guards] }); 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/accounts/forms/LoginFormComponent.vue b/resources/assets/vue/components/accounts/forms/LoginFormComponent.vue index ed097c68..50c4236d 100644 --- a/resources/assets/vue/components/accounts/forms/LoginFormComponent.vue +++ b/resources/assets/vue/components/accounts/forms/LoginFormComponent.vue @@ -2,18 +2,24 @@

+ + + + + +
- +
- +
@@ -21,7 +27,7 @@

Forgot password?

-
Sign In
+
Sign In
@@ -55,4 +61,4 @@ mixins: [loginFormValidation] } - \ No newline at end of file + diff --git a/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue b/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue index e436960a..9f49275e 100644 --- a/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue +++ b/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue @@ -33,7 +33,7 @@ viewBox="0 0 172 172" style=" fill:#000000;">
-

Company

+

Company

@@ -43,7 +43,7 @@ width="45" height="45" viewBox="0 0 172 172" style=" fill:#000000;">
-

Personal

+

Personal

@@ -62,7 +62,7 @@
- +
@@ -70,13 +70,13 @@
- +
- +
@@ -95,7 +95,7 @@
- +
@@ -103,13 +103,13 @@
- +
- +
@@ -121,19 +121,21 @@ @@ -154,7 +156,7 @@
- +
diff --git a/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue b/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue index 3bf7fa65..9c89c89d 100644 --- a/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue +++ b/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue @@ -14,56 +14,108 @@ {{error}} -
+
- - - - -
-
-
-
- - - - -
-
-
-
- - - - -
-
-
-
-
+
- - - + + +
- - - + + +
-
+
+
+ + + + +
+
+
+
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
+
-
+
@@ -122,6 +174,11 @@ type: Object, required: false, default: null + }, + currency: { + type: String, + required: false, + default: 'RMB' } }, data(){ @@ -181,4 +238,4 @@ mixins: [FormHandler] } - \ No newline at end of file + diff --git a/resources/assets/vue/components/banks/forms/PhoneAccountFormComponent.vue b/resources/assets/vue/components/banks/forms/PhoneAccountFormComponent.vue index dfec0ddb..76401d2b 100644 --- a/resources/assets/vue/components/banks/forms/PhoneAccountFormComponent.vue +++ b/resources/assets/vue/components/banks/forms/PhoneAccountFormComponent.vue @@ -14,18 +14,10 @@ {{error}}
-
-
- - - - -
-
- +
@@ -33,20 +25,17 @@
- +
-
+
-
Warning: We do not encourage to transfer to non-chinese recipient Alipay account ! Proceed Anyway.
-
-
-

Foreign name alipay may exceed Monthly / Yearly Limit , and may be unable to withdraw your funds out.

-
-
-
The risk is too high. I changed my mind.
+ + + +
@@ -54,7 +43,7 @@
{{disabled ? 'Change Recipient Account' : 'Cancel'}}
- +
@@ -102,7 +91,7 @@ bank_name: '-', holder_name: '', account_no: '', - bank_branch: '-', + bank_branch: '', country_id: this.country_id, }, englishTextWarning: false, @@ -117,15 +106,15 @@ account_no: { required }, - reference: { - required: requiredIf(function () { return this.parameters.type === 2 }) + bank_branch: { + required } } }, methods: { submitForm(){ - this.parameters.account_type = 3, - this.parameters.bank_name = '-', + this.parameters.account_type = 3; + this.parameters.bank_name = '-'; this.submit(route('api.bank.create'), 'post', this.section, true, false); }, successHandler(response){ diff --git a/resources/assets/vue/components/bookings/elements/AvailableVouchersComponent.vue b/resources/assets/vue/components/bookings/elements/AvailableVouchersComponent.vue new file mode 100644 index 00000000..45722aaf --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/AvailableVouchersComponent.vue @@ -0,0 +1,76 @@ + + + diff --git a/resources/assets/vue/components/bookings/elements/BillingComponent.vue b/resources/assets/vue/components/bookings/elements/BillingComponent.vue index 70f511ed..f24078ed 100644 --- a/resources/assets/vue/components/bookings/elements/BillingComponent.vue +++ b/resources/assets/vue/components/bookings/elements/BillingComponent.vue @@ -84,7 +84,14 @@ export default { type: 'INVOICE', supplier: null }, - documents: ['INVOICE', 'PURCHASE_ORDER', 'DELIVER_ORDER', 'SUPPLIER_DELIVER_ORDER'], + documents: [ + 'INVOICE', + 'PURCHASE_ORDER', + 'DELIVER_ORDER', + 'SUPPLIER_DELIVER_ORDER', + 'INVOICE + PO + DO', + 'INVOICE + PO + DO + SDO' + ], selectedDocumentStatus: false } }, diff --git a/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue b/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue index f08ea597..68db519b 100644 --- a/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue +++ b/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue @@ -1,131 +1,135 @@ @@ -287,6 +318,9 @@ recipientBanks: [], dropdownStatus: false, account_no: '', + step:1, + promoCode:"" + } }, computed: { @@ -317,7 +351,52 @@ 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] } - \ No newline at end of file + diff --git a/resources/assets/vue/components/bookings/elements/CurrencyOrderComponent.vue b/resources/assets/vue/components/bookings/elements/CurrencyOrderComponent.vue index ad4c3e42..a88fd5a9 100644 --- a/resources/assets/vue/components/bookings/elements/CurrencyOrderComponent.vue +++ b/resources/assets/vue/components/bookings/elements/CurrencyOrderComponent.vue @@ -40,7 +40,7 @@
Currency Order Placed
-
Are you sure you that the currency order has been placed with the supplier?
+
Are you sure that the currency order has been placed with the supplier?
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..026a76c5 --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/CustomerTransactionHistorySectionComponent.vue @@ -0,0 +1,125 @@ + + diff --git a/resources/assets/vue/components/bookings/elements/DownloadBillingWithDatesComponent.vue b/resources/assets/vue/components/bookings/elements/DownloadBillingWithDatesComponent.vue new file mode 100644 index 00000000..99be4562 --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/DownloadBillingWithDatesComponent.vue @@ -0,0 +1,83 @@ + + + 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/ListVouchersComponent.vue b/resources/assets/vue/components/bookings/elements/ListVouchersComponent.vue new file mode 100644 index 00000000..e55e5151 --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/ListVouchersComponent.vue @@ -0,0 +1,93 @@ + + + diff --git a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue index 51352679..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 }}
+
+
@@ -123,7 +133,7 @@
Requested Refund Amount
-
{{item.original_currency.short_code}} {{(Math.round((totalRequestedRefund + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+
{{item.currency.short_code}} {{(Math.round((totalRequestedRefund + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
@@ -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
@@ -200,6 +221,14 @@
+
+
+ +
+ + + +