From 36cde82a19ba93a70230105b2efcd9782f536ccd Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Thu, 14 Mar 2024 15:52:20 +0800 Subject: [PATCH 01/15] Send welcome email with voucher to user upon successful verification of customer email adddress --- .../General/Interfaces/KeyValueInterface.php | 12 ++++ app/Classes/Jobs/SendWelcomeVoucherEmail.php | 70 +++++++++++++++++++ .../ControllersLogic/CreateCustomerLogic.php | 3 +- .../UserEmailVerificationLogic.php | 29 +++++++- .../KeyValuePairObject.php | 44 ++++++++++++ .../Accounts/Services/CreatesKeyValuePair.php | 28 ++++++++ .../ListUserVouchersLogic.php | 7 ++ .../Notifications/WelcomeVoucherEmail.php | 43 ++++++++++++ .../ValueObjects/Constants/Vouchers.php | 8 +++ app/Http/Resources/KeyValueBasicResource.php | 24 +++++++ app/Http/Resources/UserRewardResource.php | 8 +++ app/Http/Resources/VoucherResource.php | 17 +++-- app/Models/KeyValuePair.php | 15 ++++ app/Models/User.php | 21 +++++- ...08_135259_create_key_value_pairs_table.php | 37 ++++++++++ .../SingleUserRewardItemComponent.vue | 7 ++ .../emails/accounts/welcome_voucher.blade.php | 8 +++ routes/web.php | 18 +++-- 18 files changed, 384 insertions(+), 15 deletions(-) create mode 100644 app/Classes/General/Interfaces/KeyValueInterface.php create mode 100644 app/Classes/Jobs/SendWelcomeVoucherEmail.php create mode 100644 app/Classes/Modules/Accounts/DataTransferObjects/KeyValuePairObject.php create mode 100644 app/Classes/Modules/Accounts/Services/CreatesKeyValuePair.php create mode 100644 app/Classes/Notifications/WelcomeVoucherEmail.php create mode 100644 app/Classes/ValueObjects/Constants/Vouchers.php create mode 100644 app/Http/Resources/KeyValueBasicResource.php create mode 100644 app/Models/KeyValuePair.php create mode 100644 database/migrations/2024_03_08_135259_create_key_value_pairs_table.php create mode 100644 resources/views/emails/accounts/welcome_voucher.blade.php diff --git a/app/Classes/General/Interfaces/KeyValueInterface.php b/app/Classes/General/Interfaces/KeyValueInterface.php new file mode 100644 index 00000000..fbee5265 --- /dev/null +++ b/app/Classes/General/Interfaces/KeyValueInterface.php @@ -0,0 +1,12 @@ +user = $user; + $this->voucher = $voucher; + $this->emailSentCount = $emailSentCount; + } + + + public function handle() + { + $currentDatetime = Carbon::now(); + $dateToCompare = Carbon::parse($this->voucher->end_date); + if (!$this->user->hasAttribute($this->voucher->code."_EMAIL_COUNT") + && $this->user->rewards->where('voucher_id', $this->voucher->id)->count() > 0 + && $currentDatetime->isBefore($dateToCompare)) + { + //Key #1 + $keyValuePairObject = new KeyValuePairObject( + $this->voucher->code."_EMAIL_COUNT", + $this->emailSentCount + ); + (App()->make(CreatesKeyValuePair::class))->execute($this->user, $keyValuePairObject); + + //Key #2 + $keyValuePairObject = new KeyValuePairObject( + $this->voucher->code."_EMAIL_DATE_".$this->emailSentCount, + Carbon::now() + ); + (App()->make(CreatesKeyValuePair::class))->execute($this->user, $keyValuePairObject); + + $this->user->notify(new WelcomeVoucherEmail($this->user, $this->voucher)); + } + } +} diff --git a/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php b/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php index fa6a9ae7..0f7bdb61 100644 --- a/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php +++ b/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php @@ -30,6 +30,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\App; use App\Classes\Modules\Segments\Services\CreatesSeasonalSegment; +use App\Classes\ValueObjects\Constants\Vouchers; class CreateCustomerLogic extends AbstractControllerLogic { @@ -159,7 +160,7 @@ class CreateCustomerLogic extends AbstractControllerLogic $this->newCustomerToVoucherifyProcessor->execute($company->id, $user, true); - $this->createVoucherProcessor->execute($user, 'WELCOME50%OFF'); + $this->createVoucherProcessor->execute($user, Vouchers::WELCOME_50_PERCENT_OFF); return $this->response($this->authenticationProcessor->execute($request, false)); diff --git a/app/Classes/Modules/Accounts/ControllersLogic/UserEmailVerificationLogic.php b/app/Classes/Modules/Accounts/ControllersLogic/UserEmailVerificationLogic.php index bae6f5eb..13a8bcc7 100644 --- a/app/Classes/Modules/Accounts/ControllersLogic/UserEmailVerificationLogic.php +++ b/app/Classes/Modules/Accounts/ControllersLogic/UserEmailVerificationLogic.php @@ -9,6 +9,9 @@ use App\Classes\Modules\Accounts\Services\CompletesEmailVerificationAttempt; use App\Classes\Modules\Accounts\Services\FetchesEmailVerificationAttempt; use App\Classes\Modules\Accounts\Services\VerifiesUser; use App\Classes\Modules\Accounts\Standards\Criteria\EmailVerificationActiveAttemptExists; +use App\Classes\Modules\Vouchers\Services\FetchesVoucher; +use App\Classes\Jobs\SendWelcomeVoucherEmail; +use App\Classes\ValueObjects\Constants\Vouchers; use App\Models\UserEmailVerification; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -36,19 +39,29 @@ class UserEmailVerificationLogic extends AbstractControllerLogic /** @var VerifiesUser */ private $verifiesUser; + /** @var SendWelcomeVoucherEmail */ + private $sendWelcomeVoucherEmail; + + /** @var FetchesVoucher */ + private $fetchesVoucher; + /** * UserEmailVerificationLogic constructor. * @param EmailVerificationActiveAttemptExists $emailVerificationActiveAttemptExists * @param CompletesEmailVerificationAttempt $completesEmailVerificationAttempt * @param FetchesEmailVerificationAttempt $fetchesEmailVerificationAttempt * @param VerifiesUser $verifiesUser + * @param SendWelcomeVoucherEmail $sendWelcomeVoucherEmail + * @param FetchesVoucher $fetchesVoucher */ - public function __construct(EmailVerificationActiveAttemptExists $emailVerificationActiveAttemptExists, CompletesEmailVerificationAttempt $completesEmailVerificationAttempt, FetchesEmailVerificationAttempt $fetchesEmailVerificationAttempt, VerifiesUser $verifiesUser) + public function __construct(EmailVerificationActiveAttemptExists $emailVerificationActiveAttemptExists, CompletesEmailVerificationAttempt $completesEmailVerificationAttempt, FetchesEmailVerificationAttempt $fetchesEmailVerificationAttempt, VerifiesUser $verifiesUser, SendWelcomeVoucherEmail $sendWelcomeVoucherEmail, FetchesVoucher $fetchesVoucher) { $this->emailVerificationActiveAttemptExists = $emailVerificationActiveAttemptExists; $this->completesEmailVerificationAttempt = $completesEmailVerificationAttempt; $this->fetchesEmailVerificationAttempt = $fetchesEmailVerificationAttempt; $this->verifiesUser = $verifiesUser; + $this->sendWelcomeVoucherEmail = $sendWelcomeVoucherEmail; + $this->fetchesVoucher = $fetchesVoucher; } /** @@ -68,9 +81,19 @@ class UserEmailVerificationLogic extends AbstractControllerLogic $this->completesEmailVerificationAttempt->execute($attempt); - $this->verifiesUser->execute($attempt->user); + $user = $attempt->user; + $this->verifiesUser->execute($user); + + // if (env('SENDING_EMAIL_WELCOME_VOUCHER_ENABLED', false)){ + if (app()->environment('production') && env('SENDING_EMAIL_WELCOME_VOUCHER_ENABLED', false)){ + try{ //In case voucher got deleted unintentionally + $voucher = $this->fetchesVoucher->execute(['code' => Vouchers::WELCOME_50_PERCENT_OFF]); + if($voucher) $this->sendWelcomeVoucherEmail::dispatch($user, $voucher, 1); + } + catch(\Exception $e){} + } return $this->response([]); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Accounts/DataTransferObjects/KeyValuePairObject.php b/app/Classes/Modules/Accounts/DataTransferObjects/KeyValuePairObject.php new file mode 100644 index 00000000..fd1eff49 --- /dev/null +++ b/app/Classes/Modules/Accounts/DataTransferObjects/KeyValuePairObject.php @@ -0,0 +1,44 @@ +key = $key; + $this->value = $value; + } + + /** + * @return string + */ + public function getKey(): string + { + return $this->key; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->value; + } + +} diff --git a/app/Classes/Modules/Accounts/Services/CreatesKeyValuePair.php b/app/Classes/Modules/Accounts/Services/CreatesKeyValuePair.php new file mode 100644 index 00000000..c963ec4e --- /dev/null +++ b/app/Classes/Modules/Accounts/Services/CreatesKeyValuePair.php @@ -0,0 +1,28 @@ +key = $object->getKey(); + $model->value = $object->getValue(); + + return $this->handler($kv->attributes(), $model); + + } +} diff --git a/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php b/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php index 16356f0f..013be9d6 100644 --- a/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php +++ b/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php @@ -4,9 +4,11 @@ namespace App\Classes\Modules\Vouchers\ControllersLogic; use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Rewards\Services\ListsUserRewards; +use App\Classes\ValueObjects\Constants\RoleTypes; use App\Http\Resources\UserRewardResource; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; class ListUserVouchersLogic extends AbstractControllerLogic { @@ -40,6 +42,11 @@ class ListUserVouchersLogic extends AbstractControllerLogic public function logic(Request $request) : JsonResponse { $query = $this->listsUserRewards->execute($this->listsUserRewards->deserializeFilters($request->input('filters'))); + + if(in_array(Auth::user()->type, RoleTypes::ADMIN_ROLES)){ + $request->merge(['isAdmin' => true]); + } + return $this->collectionResponse(UserRewardResource::collection($query)); } diff --git a/app/Classes/Notifications/WelcomeVoucherEmail.php b/app/Classes/Notifications/WelcomeVoucherEmail.php new file mode 100644 index 00000000..0800b8cc --- /dev/null +++ b/app/Classes/Notifications/WelcomeVoucherEmail.php @@ -0,0 +1,43 @@ +user = $user; + $this->voucher = $voucher; + } + + + public function toMail() + { + $this->voucher->end_date = Carbon::parse($this->voucher->end_date)->format('Y-m-d'); + $mailMessage = (new MailMessage) + ->subject('Welcome Voucher') + ->view('emails.accounts.welcome_voucher', ['user' => $this->user, 'voucher' => $this->voucher]); + + return $mailMessage; + } + + +} diff --git a/app/Classes/ValueObjects/Constants/Vouchers.php b/app/Classes/ValueObjects/Constants/Vouchers.php new file mode 100644 index 00000000..d43194f9 --- /dev/null +++ b/app/Classes/ValueObjects/Constants/Vouchers.php @@ -0,0 +1,8 @@ + $this->id, + 'key' => $this->key, + 'value' => $this->value, + ]; + } +} diff --git a/app/Http/Resources/UserRewardResource.php b/app/Http/Resources/UserRewardResource.php index ed693b4c..b988a1d3 100644 --- a/app/Http/Resources/UserRewardResource.php +++ b/app/Http/Resources/UserRewardResource.php @@ -2,6 +2,7 @@ namespace App\Http\Resources; + use Illuminate\Http\Resources\Json\JsonResource; class UserRewardResource extends JsonResource @@ -14,6 +15,13 @@ class UserRewardResource extends JsonResource */ public function toArray($request) { + $emailReminder = null; + if ($request->has('isAdmin')) { + $keyValuePairs = $this->user->attributes()->get(); + $emailReminder = KeyValueBasicResource::collection($keyValuePairs); + $this->voucher->email = $emailReminder; + } + return [ 'id' => $this->id, 'user_id' => $this->user_id, diff --git a/app/Http/Resources/VoucherResource.php b/app/Http/Resources/VoucherResource.php index 54193eeb..3f3112bb 100644 --- a/app/Http/Resources/VoucherResource.php +++ b/app/Http/Resources/VoucherResource.php @@ -2,6 +2,7 @@ namespace App\Http\Resources; +use ArrayObject; use Illuminate\Http\Resources\Json\JsonResource; class VoucherResource extends JsonResource @@ -14,9 +15,16 @@ class VoucherResource extends JsonResource */ public function toArray($request) { - $filteredRedemptions = $this->redemptions->filter(function ($redemption) { - return $redemption->transaction && $redemption->transaction->owner; - }); + $filteredRedemptions = new ArrayObject([]); + if ($request->has('filters') && str_contains($request->input('filters'), "has_active_reward")) { + $filteredRedemptions = new ArrayObject([]); + } + else{ + $filteredRedemptions = $this->redemptions->filter(function ($redemption) { + return $redemption->transaction && $redemption->transaction->owner; + }); + } + return [ 'id' => $this->id, 'name' => $this->name, @@ -25,7 +33,8 @@ class VoucherResource extends JsonResource 'value' => (float) $this->value, 'start_date' => $this->start_date, 'end_date' => $this->end_date, - 'is_redeemed' => $filteredRedemptions->count() > 0 + 'is_redeemed' => $filteredRedemptions->count() > 0, + 'email' => $this->email ? new KeyValueBasicResource($this->email->where('key', $this->code.'_EMAIL_COUNT')->first()) : null, ]; } } diff --git a/app/Models/KeyValuePair.php b/app/Models/KeyValuePair.php new file mode 100644 index 00000000..3ad6d6cd --- /dev/null +++ b/app/Models/KeyValuePair.php @@ -0,0 +1,15 @@ +morphTo(); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 4bc7ec17..7e0a4aa9 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Classes\General\Interfaces\KeyValueInterface; use App\Classes\General\Interfaces\Voucherifiable; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -26,7 +27,8 @@ class User extends AbstractModel implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, - Voucherifiable + Voucherifiable, + KeyValueInterface { use HasRoles, Notifiable, Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail, SoftDeletes; @@ -101,4 +103,21 @@ class User extends AbstractModel implements { return $this->HasMany(UserReward::class, 'user_id', 'id'); } + + public function hasAttribute(string $key, $value = null): bool + { + $query = $this->attributes()->where('key', $key); + + if ($value !== null) { + $query->where('value', $value); + } + + return $query->exists(); + } + + + public function attributes(): MorphMany + { + return $this->morphMany(KeyValuePair::class, 'owner'); + } } diff --git a/database/migrations/2024_03_08_135259_create_key_value_pairs_table.php b/database/migrations/2024_03_08_135259_create_key_value_pairs_table.php new file mode 100644 index 00000000..b952ff56 --- /dev/null +++ b/database/migrations/2024_03_08_135259_create_key_value_pairs_table.php @@ -0,0 +1,37 @@ +id(); + $table->string('owner_type'); //'user', 'order', 'transaction' + $table->unsignedBigInteger('owner_id'); + $table->string('key'); + $table->string('value'); + $table->timestamps(); + + $table->index(['owner_type', 'owner_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('key_value_pairs'); + } +} diff --git a/resources/assets/vue/components/companies/elements/SingleUserRewardItemComponent.vue b/resources/assets/vue/components/companies/elements/SingleUserRewardItemComponent.vue index 8f25ab60..9cc2409a 100644 --- a/resources/assets/vue/components/companies/elements/SingleUserRewardItemComponent.vue +++ b/resources/assets/vue/components/companies/elements/SingleUserRewardItemComponent.vue @@ -9,6 +9,10 @@

RM{{ item.voucher.value/100 }} Discount

{{ item.voucher.value }}% Discount

+
+ + +