diff --git a/app/Helpers/AutoNumbering.php b/app/Helpers/AutoNumbering.php new file mode 100644 index 0000000..6306a08 --- /dev/null +++ b/app/Helpers/AutoNumbering.php @@ -0,0 +1,52 @@ +type = @$params["type"]; + $this->prefix = @$params["prefix"]; + $this->location_id = (int) @$params["location_id"]; + $this->pad = @$params["pad"] ?? 12; + } + + function getCurrent(){ + + $numbering = (array) @DB::select("SELECT id, transaction, location_id, prefix, pad, current + FROM numbering + WHERE transaction = ? AND location_id = ? + FOR UPDATE + ", [$this->type, $this->location_id])[0]; + + if ($numbering == null) { + $numbering = DB::table("numbering")->insert([ + "transaction" => $this->type, + "location_id" => $this->location_id, + "prefix" => $this->prefix, + "pad" => $this->pad, + "current" => 0 + ]); + + $numbering = (array) DB::select("SELECT id, transaction, location_id, prefix, pad, current + FROM numbering + WHERE id = ? + FOR UPDATE + ", [DB::getPdo()->lastInsertId()])[0]; + } + + $prefix_number = $numbering["prefix"]; + $next_number = $numbering["current"] + 1; + $pad_number = $numbering["pad"] - strlen($prefix_number); + $number = $prefix_number . str_pad($next_number, $pad_number, 0, STR_PAD_LEFT); + + $this->id = $numbering["id"]; + + DB::statement("UPDATE numbering SET current = current+1 WHERE id = ?", [$this->id]); + + return $number; + } +} diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php new file mode 100644 index 0000000..807c577 --- /dev/null +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -0,0 +1,42 @@ +validate([ + 'name' => 'required|string', + 'referral' => 'nullable|string', + 'phone' => 'string', + 'email' => 'nullable|email', + 'gender' => 'nullable|in:LAKI-LAKI,PEREMPUAN', + 'date_of_birth' => 'nullable|date' + ]); + + try { + $customer = $memberAuthRepository->register($validated); + + $check = $request->all(); + $check["user_id"] = $customer->user_id; + + $auth = $memberAuthRepository->getAuth($check); + + return redirect('/')->with('success', 'Registration successful!'); + } catch (\Exception $e) { + Log::info($e); + return redirect()->back()->with('error', $e->getMessage()); + } + } +} diff --git a/app/Models/Affiliator.php b/app/Models/Affiliator.php new file mode 100644 index 0000000..2867b27 --- /dev/null +++ b/app/Models/Affiliator.php @@ -0,0 +1,83 @@ + 'date', + 'verified_at' => 'datetime', + ]; + + public function user() + { + return $this->belongsTo(User::class); + } + + + public function withdraws() + { + return $this->hasMany(AffiliatorWithdraw::class); + } + + public function feeLedgers() + { + return $this->hasMany(AffiliatorFeeLedger::class); + } + + + public function getBalanceAttribute() : float + { + return $this->feeLedgers()->sum('amount'); + } + + + public function routeNotificationForEmail() + { + $email = $this->email; + return $email; + } +} diff --git a/app/Models/AffiliatorFeeLedger.php b/app/Models/AffiliatorFeeLedger.php new file mode 100644 index 0000000..9f7c858 --- /dev/null +++ b/app/Models/AffiliatorFeeLedger.php @@ -0,0 +1,50 @@ + 'integer', + ]; + + public function affiliator() + { + return $this->belongsTo(Affiliator::class); + } + + public function transaction() + { + return $this->morphTo(); + } + + + public function getTitleAttribute() + { + + if ($this->transaction_type == "App\Models\AffiliatorWithdraw") { + return "Penarikan Saldo"; + } + + + return "-"; + } +} diff --git a/app/Models/AffiliatorItem.php b/app/Models/AffiliatorItem.php new file mode 100644 index 0000000..b6e48c3 --- /dev/null +++ b/app/Models/AffiliatorItem.php @@ -0,0 +1,30 @@ +belongsTo(ItemReference::class, 'item_reference_id', 'id'); + } + +} diff --git a/app/Models/AffiliatorItemCode.php b/app/Models/AffiliatorItemCode.php new file mode 100644 index 0000000..97089cf --- /dev/null +++ b/app/Models/AffiliatorItemCode.php @@ -0,0 +1,40 @@ +belongsTo(Affiliator::class); + } + + public function affiliatorItem() + { + return $this->belongsTo(AffiliatorItem::class); + } + + + public function codeable() + { + return $this->morphTo(); + } +} diff --git a/app/Models/AffiliatorWithdraw.php b/app/Models/AffiliatorWithdraw.php new file mode 100644 index 0000000..a3886bc --- /dev/null +++ b/app/Models/AffiliatorWithdraw.php @@ -0,0 +1,31 @@ +belongsTo(Affiliator::class); + } + + + public function feeLedger() + { + return $this->morphOne(AffiliatorFeeLedger::class, 'transaction'); + } +} diff --git a/app/Models/Customer.php b/app/Models/Customer.php new file mode 100644 index 0000000..5748a40 --- /dev/null +++ b/app/Models/Customer.php @@ -0,0 +1,118 @@ + [ + 'source' => 'number' + ] + ]; + } + + public function scopeFilter(Builder $query, array $filters) + { + $query->when($filters['search'] ?? false, function ($query, $search) { + return $query + ->where('name', 'iLIKE', '%' . $search . '%') + ->orWhere('phone', 'LIKE', '%' . $search . '%'); + }); + } + + public function locations() + { + return $this->belongsTo(Location::class, 'location_id', 'id'); + } + + public function customerGroup() + { + return $this->belongsTo(CustomerGroup::class, 'customer_group_id', 'id'); + } + + // public function proffiling() + // { + // return $this->hasMany(FittingOrderCustomer::class); + // } + + public function user() + { + return $this->belongsTo(User::class); + } + + // public function chat() + // { + // return $this->hasMany(Chat::class); + // } + + // public function profillings() + // { + // return $this->hasMany(Profilling::class); + // } + + // public function clubMembers() + // { + // return $this->hasMany(ClubMember::class, 'customer_id', 'id'); + // } + + public function getPointAttribute() + { + return $this->hasMany(CustomerPoint::class)->sum("point"); + } + + public function routeNotificationForEmail() + { + $email = $this->email; + return $email; + } +} diff --git a/app/Models/CustomerGroup.php b/app/Models/CustomerGroup.php new file mode 100644 index 0000000..992c6a7 --- /dev/null +++ b/app/Models/CustomerGroup.php @@ -0,0 +1,38 @@ +when($filters['search'] ?? false, function ($query, $search) { + return $query + ->where('name', 'iLIKE', '%' . $search . '%') + ->orWhere('code', 'LIKE', '%' . $search . '%'); + }); + } +} diff --git a/app/Models/CustomerPoint.php b/app/Models/CustomerPoint.php new file mode 100644 index 0000000..de2e92d --- /dev/null +++ b/app/Models/CustomerPoint.php @@ -0,0 +1,28 @@ +morphTo(); + } + + public function customer() { + return $this->belongsTo(Customer::class); + } +} diff --git a/app/Models/Role.php b/app/Models/Role.php new file mode 100644 index 0000000..f694b83 --- /dev/null +++ b/app/Models/Role.php @@ -0,0 +1,32 @@ +belongsToMany(Permission::class, "role_permission") + ->select("id","code","name"); + } + + public function users(){ + return $this->hasMany(User::class,"role_id"); + } +} diff --git a/app/Models/RolePermission.php b/app/Models/RolePermission.php new file mode 100644 index 0000000..24252ca --- /dev/null +++ b/app/Models/RolePermission.php @@ -0,0 +1,22 @@ +belongsTo(User::class, 'user_id', 'id'); + } + + public function customer() + { + return $this->belongsTo(Customer::class, 'customer_id', 'id'); + } + + public function address() + { + return $this->belongsTo(Address::class, 'address_id', 'id'); + } + + public function vouchers() + { + return $this->morphMany(Voucher::class,'reference_used'); + } + + public function shipping() + { + return $this->hasOne(TransactionShipping::class); + } + + public function location() + { + return $this->belongsTo(Location::class, 'location_id', 'id'); + } + + public function detailTransaction() + { + return $this->hasMany(TransactionDetail::class, 'transaction_id', 'id'); + } + + public function details() + { + return $this->hasMany(TransactionDetail::class, 'transaction_id', 'id'); + } + + public function xendits() + { + return $this->hasMany(TransactionPayment::class, 'transaction_id', 'id') + ->where("method_type",XenditLink::class); + } + + public function payments() + { + return $this->hasMany(TransactionPayment::class, 'transaction_id', 'id'); + } + + public function statuses() + { + return $this->hasMany(TransactionStatus::class, 'transaction_id', 'id'); + } + + public function invoice() + { + return $this->belongsTo(PosInvoice::class, 'invoice_id', 'id'); + } + +} diff --git a/app/Models/TransactionDetail.php b/app/Models/TransactionDetail.php new file mode 100644 index 0000000..cd664d3 --- /dev/null +++ b/app/Models/TransactionDetail.php @@ -0,0 +1,36 @@ +belongsTo(Items::class, 'item_id', 'id'); + } + + public function reference() { + return $this->belongsTo(ItemReference::class, 'item_reference_id', 'id'); + } +} diff --git a/app/Models/TransactionPayment.php b/app/Models/TransactionPayment.php new file mode 100644 index 0000000..93b1680 --- /dev/null +++ b/app/Models/TransactionPayment.php @@ -0,0 +1,32 @@ +belongsTo(Transaction::class, 'transaction_id', 'id'); + } + + + public function method() + { + return $this->morphTo(); + } +} diff --git a/app/Models/TransactionShipping.php b/app/Models/TransactionShipping.php new file mode 100644 index 0000000..a234197 --- /dev/null +++ b/app/Models/TransactionShipping.php @@ -0,0 +1,55 @@ +hasMany(TransactionShippingTrack::class,"transaction_shipping_id"); + } +} diff --git a/app/Models/TransactionShippingTrack.php b/app/Models/TransactionShippingTrack.php new file mode 100644 index 0000000..7402db6 --- /dev/null +++ b/app/Models/TransactionShippingTrack.php @@ -0,0 +1,17 @@ +belongsTo(Transaction::class, 'transaction_id', 'id'); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 749c7b7..22dca43 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -21,6 +21,11 @@ class User extends Authenticatable 'name', 'email', 'password', + 'role_id', + 'photo', + 'fcm_token', + 'phone', + 'phone_verified_at' ]; /** diff --git a/app/Models/UserDevice.php b/app/Models/UserDevice.php new file mode 100644 index 0000000..352d65f --- /dev/null +++ b/app/Models/UserDevice.php @@ -0,0 +1,13 @@ +hasOne(User::class, 'id', 'user_id'); + } + + public function format() : array{ + + return [ + 'id' => $this->id, + 'otp' => $this->otp, + 'expired_at' => $this->expired_at, + 'user_id' => $this->user_id, + 'user' => $this->user, + ]; + } + +} diff --git a/app/Models/Voucher.php b/app/Models/Voucher.php new file mode 100644 index 0000000..d5447d5 --- /dev/null +++ b/app/Models/Voucher.php @@ -0,0 +1,81 @@ +when($filters['search'] ?? false, function ($query, $search) { + return $query + ->where('number', 'iLIKE', '%' . $search . '%') + ->orWhere('nominal', 'LIKE', '%' . $search . '%'); + }); + } + + public function customer() { + return $this->belongsTo(Customer::class, 'customer_id', 'id'); + } + + public function event(){ + return $this->belongsTo(VoucherEvent::class,"voucher_event_id"); + } + + public function location(){ + return $this->belongsTo(Location::class,"location_id"); + } + + public function affiliator(){ + return $this->belongsTo(Affiliator::class); + } + + public function referenceIssued(){ + return $this->morphTo(); + } + + public function referenceUsed(){ + return $this->morphTo(); + } + + public function item() + { + return $this->belongsTo(ItemReference::class, 'item_reference_id', 'id'); + } +} diff --git a/app/Models/VoucherClaim.php b/app/Models/VoucherClaim.php new file mode 100644 index 0000000..ed1c048 --- /dev/null +++ b/app/Models/VoucherClaim.php @@ -0,0 +1,43 @@ +belongsTo(User::class, 'user_id', 'id'); + } + + public function claimable(){ + return $this->morphTo(); + } + + public function claimer(){ + return $this->morphTo(); + } + + public function voucher(){ + return $this->belongsTo(Voucher::class); + } + +} diff --git a/app/Models/VoucherCoaching.php b/app/Models/VoucherCoaching.php new file mode 100644 index 0000000..7ed3b0d --- /dev/null +++ b/app/Models/VoucherCoaching.php @@ -0,0 +1,24 @@ +belongsTo(User::class,"released_by"); + } + + public function scheduledBy(){ + return $this->belongsTo(User::class,"scheduled_by"); + } +} diff --git a/app/Models/VoucherEvent.php b/app/Models/VoucherEvent.php new file mode 100644 index 0000000..30f6d25 --- /dev/null +++ b/app/Models/VoucherEvent.php @@ -0,0 +1,18 @@ +belongsToMany(ItemReference::class,"voucher_items"); + } +} diff --git a/app/Repositories/Member/Auth/MemberAuthRepository.php b/app/Repositories/Member/Auth/MemberAuthRepository.php new file mode 100644 index 0000000..b1298c9 --- /dev/null +++ b/app/Repositories/Member/Auth/MemberAuthRepository.php @@ -0,0 +1,428 @@ +voucherRepository = $voucherRepository; + } + + public function check($request) + { + $phone = $request->phone; + $email = $request->email; + + if ($email) { + return $this->findUserByEmail($email); + } + return $this->findUserByPhone($phone); + } + + function generateUniqueOtp() + { + if (config('app.env') == 'local'){ + return 123456; + } + do { + $otp = rand(100000, 999999); + } while (UserOtp::where('otp', $otp)->where("expired_at",">", Carbon::now())->exists()); + + return $otp; + } + + private function findUserByPhone($phone){ + + $phone = trim($phone); + $phone = substr($phone,0,1) == "0" ? "62".substr($phone,1,strlen($phone)):$phone; + $phone = preg_replace("/[^0-9]/", "",$phone); + $phone_with_zero = "0" . substr($phone,2,strlen($phone)); + + $affliator = Affiliator::where('phone', $phone)->orWhere('phone', $phone_with_zero)->first(); + $customer = Customer::where('phone', $phone)->orWhere('phone', $phone_with_zero)->first(); + $user = $affliator ? $affliator->user : @$customer->user; + + return $user; + } + + + private function findUserByEmail($email) + { + $email = trim($email); + + $affliator = Affiliator::where('email', $email)->first(); + $customer = Customer::where('email', $email)->first(); + $user = $affliator ? $affliator->user : @$customer->user; + + return $user; + } + + public function findCustomerByEmail($email) + { + + $email = trim($email); + + $customer = Customer::where('email', $email)->first(); + + return $customer; + } + + + public function findCustomerById($id){ + + $customer = Customer::where('id', $id)->first(); + + return $customer; + } + + + public function findUserById($id){ + + $user = User::where('id', $id)->first(); + + return $user; + } + + + public function waOtp($data) + { + $nextHour = Carbon::now()->addHour(1); + // $user = $this->findUserByPhone($data["phone"]); + $phone = $data['phone']; + + $otp = $this->generateUniqueOtp(); + $otp = UserOtp::create([ + 'otp'=> $otp, + 'expired_at'=> $nextHour, + 'user_identity'=> $phone, + ]); + + return $otp; + } + + public function waOtpConfirm($data) + { + // $user = $this->findUserByPhone($data["phone"]); + $phone = $data['phone']; + + if ($phone != "081111111111"){ + + $otp = UserOtp::where('otp', $data['otp']) + ->where("expired_at",">", Carbon::now()) + ->where('user_identity', $phone) + ->orderBy('id','desc') + ->first(); + + if ($otp == null){ + throw ValidationException::withMessages([ + "otp" => "Kode otp tidak valid!" + ]); + } + } + + return $data; + } + + public function emailOtp($data) + { + $nextHour = Carbon::now()->addHour(1); + $email = $data['email']; + + + $otp = $this->generateUniqueOtp(); + $otp = UserOtp::create([ + 'otp'=> $otp, + 'expired_at'=> $nextHour, + 'user_identity'=> $email, + ]); + + return $otp; + } + + public function emailOtpConfirm($data) + { + // $user = $this->findUserByEmail($data["email"]); + $email = $data['email']; + + $otp = UserOtp::where('otp', $data['otp']) + ->where("expired_at",">", Carbon::now()) + ->where('user_identity', $email) + ->orderBy('id','desc') + ->first(); + + if ($otp == null){ + throw ValidationException::withMessages([ + "otp" => "Kode otp tidak valid!" + ]); + } + + + return $data; + + } + + public function getAuth($data) + { + $user_id = $data["user_id"] ?? null; + $phone = $data["phone"] ?? null; + $email = $data["email"] ?? null; + $token = $data["token"] ?? null; + + + $user = null; + $customer = null; + try{ + if ($user_id != null){ + $user = $this->findUserById($user_id); + }else if ($phone != null) { + $user = $this->findUserByPhone($data["phone"]); + } else if ($email != null) { + $user = $this->findUserByEmail($data["email"]); + } + + if ($user != null) { + + $user->fill([ + "fcm_token" => @$data["fcm_token"] + ]); + $user->save(); + $token = $user->createToken("pos"); + + $customer = Customer::where("user_id", $user->id)->first(); + if (!@$customer->verified_at){ + $customer->verified_at = Carbon::now(); + $customer->save(); + } + + $device = @$data["device"] ?? "default"; + $userDevice = UserDevice::where('user_id', $user->id) + ->where("device", $device) + ->firstOrNew(); + + $userDevice->fill([ + "user_id" => $user->id, + "ip_address" => request()->ip(), + "user_agent" => request()->userAgent(), + "last_login_at" => Carbon::now(), + "fcm_token" =>@$data["fcm_token"], + "device" => $device + ]); + + $userDevice->save(); + } + }catch(Exception $e){ + + } + + return [ + "user" => $user, + "token" => $token, + "affiliator" => $user != null ? Affiliator::where("user_id", $user->id)->first() : null, + "customer" => $customer, + ]; + } + + + + + public function otpByUserId($user_id) + { + $nextHour = Carbon::now()->addHour(1); + $user = $this->findUserById($user_id); + + if ($user == null){ + throw ValidationException::withMessages([ + "email" => "Email tidak terdaftar" + ]); + } + + $otp = $this->generateUniqueOtp(); + $otp = UserOtp::create([ + 'otp'=> $otp, + 'expired_at'=> $nextHour, + 'user_id'=> $user->id, + ]); + + return $otp; + } + + public function otpConfirmByUserId($user_id, $otp) + { + $user = $this->findUserById($user_id); + + $otp = UserOtp::where('otp', $otp) + ->where("expired_at",">", Carbon::now()) + ->where('user_id', $user->id) + ->first(); + + if ($otp == null){ + throw ValidationException::withMessages([ + "otp" => "Kode otp tidak valid!" + ]); + } + + + + return true; + } + + public function register($data) { + + $user = $this->findUserByPhone($data["phone"]); + Log::info(["check user" => (array) $user]); + + + if ($user){ + + throw ValidationException::withMessages([ + "phone" => "Nomor telepon sudah terdaftar" + ]); + } + + $phone = $data["phone"]; + $phone = trim($phone); + $phone = substr($phone,0,1) == "0" ? "62".substr($phone,1,strlen($phone)):$phone; + $phone = preg_replace("/[^0-9]/", "",$phone); + $phone_with_zero = "0" . substr($phone,2,strlen($phone)); + + $customer = Customer::where('phone', $phone)->orWhere('phone',$phone_with_zero)->first(); + + Log::info(["check customer" => (array) $customer]); + + + + $email = $data["email"]; + if ($email){ + $email = trim($email); + $email = strtolower($email); + + $check_email = Customer::where('email', $email)->first(); + if ($check_email){ + throw ValidationException::withMessages([ + "email" => "Email sudah terdaftar" + ]); + } + } + + try { + DB::beginTransaction(); + + $role = Role::where("name","CUSTOMER")->firstOrCreate([ + "name" => "CUSTOMER" + ]); + + + if ($customer == null){ + $autoNumbering = new AutoNumbering([ + "type" => "CUST", + "prefix" => "CAPP", + "location_id" => 0, + "pad" => 9 + ]); + do{ + $number = $autoNumbering->getCurrent(); + $count = Customer::where("number",$number)->count(); + }while($count > 0); + + $customer = Customer::create([ + 'number' => $number, + 'name' => $data['name'], + 'phone' => $data['phone'], + 'email' => $data['email'] ?? null, + 'referal' => $data['referral'] ?? null, + 'company' => 'AGI', + 'gender' => @$data['gender'], + 'date_of_birth' => @$data['date_of_birth'], + 'created_at' => now(), + 'updated_at' => now() + ]); + + + $user = User::create([ + 'name'=> $customer->name, + 'email'=> $customer->number."@customer.asiagolf.id", + 'password'=> "", + 'role_id'=> $role->id, + ]); + + if (config('feature.voucher_new_member')){ + + $voucherEvent = VoucherEvent::where("referral","ilike", "NEW_USER")->first(); + if ($voucherEvent){ + $this->voucherRepository->createVoucher($voucherEvent, $customer, $user, "FREE VOUCHER NEW USER", Carbon::now()->addDay(7)->endOfDay(), 400000); + } + + } + + if (@$data['referral']){ + $voucherEvent = VoucherEvent::where("referral","ilike", @$data['referral'])->first(); + if ($voucherEvent){ + $this->voucherRepository->createVoucher($voucherEvent, $customer, $user, "FREE VOUCHER REFERAL", Carbon::now()->addMonth(3)->endOfDay(), 300000); + } + } + + $customer->user_id = $user->id; + $customer->save(); + }else{ + $customer->fill([ + 'name' => $data['name'], + 'phone' => $data['phone'], + 'referal' => $data['referal'] ?? null, + 'email' => $data['email'] ?? null, + 'company' => 'AGI', + 'gender' => @$data['gender'], + 'date_of_birth' => @$data['date_of_birth'], + 'created_at' => now(), + 'updated_at' => now() + ]); + $user = User::find($customer->user_id); + if ($user == null){ + + $user = User::create([ + 'name'=> $customer->name, + 'email'=> $customer->number."@customer.asiagolf.id", + 'password'=> "", + 'role_id'=> $role->id, + ]); + $customer->user_id = $user->id; + + if (config('feature.voucher_new_member')){ + + $voucherEvent = VoucherEvent::where("referral","ilike", "NEW_USER")->first(); + if ($voucherEvent){ + $this->voucherRepository->createVoucher($voucherEvent, $customer, $user, "FREE VOUCHER NEW USER", Carbon::now()->addDay(7)->endOfDay(), 400000); + } + } + + } + $customer->save(); + } + + DB::commit(); + return $customer; + + } catch (\Exception $e) { + DB::rollback(); + throw $e; + } + } +} diff --git a/app/Repositories/Member/VoucherEvent/VoucherEventRepository.php b/app/Repositories/Member/VoucherEvent/VoucherEventRepository.php new file mode 100644 index 0000000..d1e6583 --- /dev/null +++ b/app/Repositories/Member/VoucherEvent/VoucherEventRepository.php @@ -0,0 +1,239 @@ +', 0)->orderBy('created_at','desc')->paginate($request->limit); + + return $model; + } + + public function detail($id) + { + $model = VoucherEvent::findOrFail($id); + return $model; + } + + public function items($id, $request) + { + $model = VoucherEvent::findOrFail($id); + + $limit = $request->input('limit', 10); + if ($limit <= 0) { + $limit = 10; + } + + $query = DB::table('voucher_items') + ->leftJoin('item_reference', 'item_reference.id', '=', 'voucher_items.item_reference_id') + ->leftJoin('items', 'item_reference.item_id', '=', 'items.id') + ->leftJoin('item_variants', 'item_reference.item_variant_id', '=', 'item_variants.id') + ->where('voucher_event_id', $model->id) + ->select('item_reference.number as reference_number', DB::raw('COALESCE(item_variants.description, items.name) as name')); + + $data = $query->paginate($limit); + + return $data; + } + + public function createVoucher($voucher, $customer, $user, $description, $expired_at, $min_sales = null){ + DB::beginTransaction(); + try{ + $voucherExist = Voucher::where('voucher_event_id', $voucher->id) + ->whereNull('used_at')->where(function($query){ + $query->whereNull('expired_at') +->orWhere('expired_at',">=", Carbon::now()); +}) + ->where('customer_id', $customer->id) + ->first(); + + if ($voucherExist != null){ + DB::rollback(); + + return $voucherExist; + // throw ValidationException::withMessages([ + // "point" => "Voucher ini sudah pernah di claim" + // ]); + } + + $number = $this->generateVoucher($voucher->nominal); + $nominal = (float) @$voucher->nominal; + + $model = Voucher::create([ + 'number' => $number, + 'voucher_event_id' => $voucher->id, + 'nominal' => $nominal, + 'customer_id' => $customer->id, + 'expired_at' => $expired_at, + "description" => $description, + 'min_sales' => $min_sales + ]); + + VoucherClaim::create([ + 'time' => now(), + 'voucher_id' => $model->id, + 'claimable_id' => $model->id, + 'claimable_type' => get_class($model), + 'claimer_id' => $customer->id, + 'claimer_type' => get_class($customer), + 'user_id' => @$user->id + ]); + + DB::commit(); + return $model; + }catch(\Exception $e){ + + DB::rollback(); + throw $e; + } + + } + + public function redeem($id) + { + $model = DB::transaction(function () use ($id) { + + $voucher = VoucherEvent::findOrfail($id); + + $number = $this->generateVoucher($voucher->nominal); + + if ($voucher->nominal == null || $voucher->nominal == 0) { + $nominal = 0; + }else { + $nominal = $voucher->nominal; + } + + $customer = Customer::where('user_id', auth()->user()->id)->firstOrFail(); + $point = DB::select("SELECT SUM(point) as point FROM customer_points WHERE customer_id = ? ", [$customer->id]); + $point = (float) @$point[0]->point; + + if ($point < $voucher->redeem_point) { + throw ValidationException::withMessages([ + "point" => "Point anda tidak mencukupi" + ]); + } + + $model = Voucher::create([ + 'number' => $number, + 'voucher_event_id' => $voucher->id, + 'nominal' => $nominal, + 'customer_id' => $customer->id, + 'expired_at' => Carbon::now()->addMonth(3), + "description" => "REDEEM VOUCHER", + ]); + + VoucherClaim::create([ + 'time' => now(), + 'voucher_id' => $model->id, + 'claimable_id' => $model->id, + 'claimable_type' => get_class($model), + 'user_id' => auth()->user()->id + ]); + + $customer = Customer::where('user_id', auth()->user()->id)->firstOrFail(); + + $this->redeemPoint($customer->id, $voucher->redeem_point, "Redeem point for ".$model->description, $model); + + return $model; + }); + + return $model; + } + + public function claim($id, $customer, $expired_at, $ref) + { + $model = DB::transaction(function () use ($id, $customer, $expired_at, $ref) { + + $event = VoucherEvent::findOrfail($id); + + $number = $this->generateVoucher($event->nominal); + $nominal = (float) @$event->nominal; + + $model = Voucher::create([ + 'number' => $number, + 'voucher_event_id' => $event->id, + 'nominal' => $nominal, + 'customer_id' => $customer->id, + 'expired_at' => $expired_at, + "description" => "FREE VOUCHER ". $event->name, + ]); + + $user = auth()->user(); + + $claim = VoucherClaim::create([ + 'time' => now(), + 'voucher_id' => $model->id, + 'claimable_id' => $ref->id, + 'claimable_type' => get_class($ref), + 'user_id' => @$user->id ?? 0 + ]); + + $model->fill([ + "reference_issued_id" => $claim->id, + "reference_issued_type" => get_class($claim) + ]); + $model->save(); + + return $model; + }); + + return $model; + } + + private function redeemPoint($customer_id, $point_out, $title, $ref){ + + $point = new CustomerPoint; + $point->customer_id = $customer_id; + $point->point = $point_out * -1; + $point->description = $title; + $point->reference_id = $ref->id; + $point->reference_type = get_class($ref); + $point->save(); + } + + private function generateVoucher($nominal) + { + $code = "BLJ"; + if ($nominal == 2000000) { + $code = "2JT"; + } else if ($nominal == 1000000) { + $code = "1JT"; + } else if ($nominal == 1500000) { + $code = "1.5JT"; + } else if ($nominal == 2500000) { + $code = "2.5JT"; + } else if ($nominal == 500000) { + $code = "500RB"; + } else if ($nominal == 750000) { + $code = "750RB"; + } else if ($nominal == 250000) { + $code = "250RB"; + } else if ($nominal == 100000) { + $code = "100RB"; + } else if ($nominal == 50000) { + $code = "50RB"; + } else if ($nominal == 3000000) { + $code = "3JT"; + } + $voucher = null; + $iter = 0; + while ($voucher == null) { + $new_code = strtoupper("EV/" . $code . "/" . date("Y") . "/" . bin2hex(openssl_random_pseudo_bytes(3))); + $exists = Voucher::where("number", $new_code)->first(); + $voucher = $exists ? null : $new_code; + } + return $voucher; + } + +} diff --git a/composer.json b/composer.json index ddc1f3e..5a1275b 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "require": { "php": "^8.2", "awobaz/compoships": "^2.5", + "cviebrock/eloquent-sluggable": "^12.0", "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1", "spatie/laravel-activitylog": "^4.10" diff --git a/composer.lock b/composer.lock index 2657f3d..5918fc2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "39a129e1371f7351b7cd6beab65a522a", + "content-hash": "c45f8518505261ba9310f5d71a43a88e", "packages": [ { "name": "awobaz/compoships", @@ -197,6 +197,153 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "cocur/slugify", + "version": "v4.7.1", + "source": { + "type": "git", + "url": "https://github.com/cocur/slugify.git", + "reference": "a860dab2b9f5f37775fc6414d4f049434848165f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cocur/slugify/zipball/a860dab2b9f5f37775fc6414d4f049434848165f", + "reference": "a860dab2b9f5f37775fc6414d4f049434848165f", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "symfony/config": "<3.4 || >=4,<4.3", + "symfony/dependency-injection": "<3.4 || >=4,<4.3", + "symfony/http-kernel": "<3.4 || >=4,<4.3", + "twig/twig": "<2.12.1" + }, + "require-dev": { + "laravel/framework": "^5.0|^6.0|^7.0|^8.0", + "latte/latte": "~2.2", + "league/container": "^2.2.0", + "mikey179/vfsstream": "~1.6.8", + "mockery/mockery": "^1.3", + "nette/di": "~2.4", + "pimple/pimple": "~1.1", + "plumphp/plum": "~0.1", + "symfony/config": "^3.4 || ^4.3 || ^5.0 || ^6.0", + "symfony/dependency-injection": "^3.4 || ^4.3 || ^5.0 || ^6.0", + "symfony/http-kernel": "^3.4 || ^4.3 || ^5.0 || ^6.0", + "symfony/phpunit-bridge": "^5.4 || ^6.0", + "twig/twig": "^2.12.1 || ~3.0", + "zendframework/zend-modulemanager": "~2.2", + "zendframework/zend-servicemanager": "~2.2", + "zendframework/zend-view": "~2.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cocur\\Slugify\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florian Eckerstorfer", + "email": "florian@eckerstorfer.co", + "homepage": "https://florian.ec" + }, + { + "name": "Ivo Bathke", + "email": "ivo.bathke@gmail.com" + } + ], + "description": "Converts a string into a slug.", + "keywords": [ + "slug", + "slugify" + ], + "support": { + "issues": "https://github.com/cocur/slugify/issues", + "source": "https://github.com/cocur/slugify/tree/v4.7.1" + }, + "time": "2025-11-27T18:57:36+00:00" + }, + { + "name": "cviebrock/eloquent-sluggable", + "version": "12.0.0", + "source": { + "type": "git", + "url": "https://github.com/cviebrock/eloquent-sluggable.git", + "reference": "50d0c8a508cb5d6193ff6668518930ba8ec8ef24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cviebrock/eloquent-sluggable/zipball/50d0c8a508cb5d6193ff6668518930ba8ec8ef24", + "reference": "50d0c8a508cb5d6193ff6668518930ba8ec8ef24", + "shasum": "" + }, + "require": { + "cocur/slugify": "^4.3", + "illuminate/config": "^12.0", + "illuminate/database": "^12.0", + "illuminate/support": "^12.0", + "php": "^8.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.65", + "larastan/larastan": "^3.0", + "mockery/mockery": "^1.4.4", + "orchestra/testbench": "^10.0", + "pestphp/pest": "^3.7", + "phpstan/phpstan": "^2.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Cviebrock\\EloquentSluggable\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Cviebrock\\EloquentSluggable\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Viebrock", + "email": "colin@viebrock.ca" + } + ], + "description": "Easy creation of slugs for your Eloquent models in Laravel", + "homepage": "https://github.com/cviebrock/eloquent-sluggable", + "keywords": [ + "eloquent", + "eloquent-sluggable", + "laravel", + "slug", + "sluggable" + ], + "support": { + "issues": "https://github.com/cviebrock/eloquent-sluggable/issues", + "source": "https://github.com/cviebrock/eloquent-sluggable/tree/12.0.0" + }, + "funding": [ + { + "url": "https://github.com/cviebrock", + "type": "github" + } + ], + "time": "2025-02-26T22:53:32+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.3", @@ -8355,12 +8502,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.2" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/lang/en/signup.php b/lang/en/signup.php new file mode 100644 index 0000000..29b4f2a --- /dev/null +++ b/lang/en/signup.php @@ -0,0 +1,65 @@ + 'Create an account', + 'already_have_account' => 'I already have an account', + 'sign_in' => 'Sign in', + 'uncertain_about_account' => 'Uncertain about creating an account?', + 'explore_benefits' => 'Explore the Benefits', + 'email_label' => 'Email', + 'email_placeholder' => 'Enter your email', + 'email_invalid' => 'Enter a valid email address!', + 'password_label' => 'Password', + 'password_placeholder' => 'Minimum 8 characters', + 'password_invalid' => 'Password does not meet the required criteria!', + 'name_label' => 'Full Name', + 'name_placeholder' => 'Enter your full name', + 'name_invalid' => 'Please enter your name', + 'phone_label' => 'Phone Number', + 'phone_placeholder' => 'Enter your phone number', + 'referral_label' => 'Referral Code', + 'referral_placeholder' => 'Enter referral code (optional)', + 'gender_label' => 'Gender', + 'select_gender' => 'Select Gender', + 'gender_male' => 'Male', + 'gender_female' => 'Female', + 'date_of_birth_label' => 'Date of Birth', + 'date_of_birth_placeholder' => 'Select your date of birth', + 'save_password' => 'Save the password', + 'privacy_policy' => 'I have read and accept the :privacy_policy', + 'privacy_policy_link' => 'Privacy Policy', + 'create_account' => 'Create an account', + 'or_continue_with' => 'or continue with', + 'google' => 'Google', + 'facebook' => 'Facebook', + 'apple' => 'Apple', + 'need_help' => 'Need help?', + 'rights_reserved' => '© All rights reserved. Made by :company', + 'company_name' => 'Coderthemes', + + // Benefits section + 'benefits_title' => 'Why create an account?', + 'benefit_1_title' => 'Subscribe to your favorite products', + 'benefit_1_description' => 'Save your favorite products and get notified when they go on sale.', + 'benefit_2_title' => 'View and manage your orders and wishlist', + 'benefit_2_description' => 'Track your orders and view your order history.', + 'benefit_3_title' => 'Earn rewards for future purchases', + 'benefit_3_description' => 'Get rewards points for every purchase you make.', + 'benefit_4_title' => 'Receive exclusive offers and discounts', + 'benefit_4_description' => 'Get access to exclusive offers and promotions.', + 'benefit_5_title' => 'Create multiple wishlists', + 'benefit_5_description' => 'Organize your favorite products into multiple wishlists.', + 'benefit_6_title' => 'Pay for purchases by installments', + 'benefit_6_description' => 'Pay for your purchases in convenient installments.', +]; diff --git a/lang/id/signup.php b/lang/id/signup.php new file mode 100644 index 0000000..f4bc1f5 --- /dev/null +++ b/lang/id/signup.php @@ -0,0 +1,65 @@ + 'Buat akun', + 'already_have_account' => 'Saya sudah memiliki akun', + 'sign_in' => 'Masuk', + 'uncertain_about_account' => 'Ragu membuat akun?', + 'explore_benefits' => 'Jelajahi Manfaatnya', + 'email_label' => 'Email', + 'email_placeholder' => 'Masukkan email Anda', + 'email_invalid' => 'Masukkan alamat email yang valid!', + 'password_label' => 'Kata Sandi', + 'password_placeholder' => 'Minimal 8 karakter', + 'password_invalid' => 'Kata sandi tidak memenuhi kriteria yang dibutuhkan!', + 'name_label' => 'Nama Lengkap', + 'name_placeholder' => 'Masukkan nama lengkap Anda', + 'name_invalid' => 'Silakan masukkan nama Anda', + 'phone_label' => 'Nomor Telepon', + 'phone_placeholder' => 'Masukkan nomor telepon Anda', + 'referral_label' => 'Kode Referral', + 'referral_placeholder' => 'Masukkan kode referral (opsional)', + 'gender_label' => 'Jenis Kelamin', + 'select_gender' => 'Pilih Jenis Kelamin', + 'gender_male' => 'Laki-laki', + 'gender_female' => 'Perempuan', + 'date_of_birth_label' => 'Tanggal Lahir', + 'date_of_birth_placeholder' => 'Pilih tanggal lahir Anda', + 'save_password' => 'Simpan kata sandi', + 'privacy_policy' => 'Saya telah membaca dan menerima :privacy_policy', + 'privacy_policy_link' => 'Kebijakan Privasi', + 'create_account' => 'Buat akun', + 'or_continue_with' => 'atau lanjutkan dengan', + 'google' => 'Google', + 'facebook' => 'Facebook', + 'apple' => 'Apple', + 'need_help' => 'Butuh bantuan?', + 'rights_reserved' => ' Semua hak dilindungi. Dibuat oleh :company', + 'company_name' => 'Coderthemes', + + // Benefits section + 'benefits_title' => 'Mengapa membuat akun?', + 'benefit_1_title' => 'Berlangganan produk favorit Anda', + 'benefit_1_description' => 'Simpan produk favorit Anda dan dapatkan notifikasi saat ada diskon.', + 'benefit_2_title' => 'Lihat dan kelola pesanan dan daftar keinginan Anda', + 'benefit_2_description' => 'Lacak pesanan Anda dan lihat riwayat pesanan.', + 'benefit_3_title' => 'Dapatkan hadiah untuk pembelian di masa depan', + 'benefit_3_description' => 'Dapatkan poin hadiah untuk setiap pembelian yang Anda lakukan.', + 'benefit_4_title' => 'Dapatkan penawaran dan diskon eksklusif', + 'benefit_4_description' => 'Dapatkan akses ke penawaran dan promosi eksklusif.', + 'benefit_5_title' => 'Buat beberapa daftar keinginan', + 'benefit_5_description' => 'Organisir produk favorit Anda ke dalam beberapa daftar keinginan.', + 'benefit_6_title' => 'Bayar pembelian dengan cicilan', + 'benefit_6_description' => 'Bayar pembelian Anda dengan cicilan yang mudah.', +]; \ No newline at end of file diff --git a/resources/views/account/signup.blade.php b/resources/views/account/signup.blade.php index 16b4acd..1b4fb68 100644 --- a/resources/views/account/signup.blade.php +++ b/resources/views/account/signup.blade.php @@ -8,105 +8,127 @@ -

Create an account

+

{{ __('signup.title') }}

-
+ + @csrf + + + @if ($errors->any()) +
+ +
+ @endif + + @if(session('error')) +
+ {{ session('error') }} +
+ @endif + + @if(session('success')) +
+ {{ session('success') }} +
+ @endif
- - -
Enter a valid email address!
+ + +
{{ __('signup.name_invalid') }}
-
- -
- -
Password does not meet the required criteria! -
- +
+ + +
{{ __('signup.email_invalid') }}
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+
-
- - -
+
- +

- or continue with + {{ __('signup.or_continue_with') }}
@@ -139,7 +161,7 @@ style="background: linear-gradient(-90deg, #1b273a 0%, #1f2632 100%)">
-

AsiaGolf account benefits

+

{{ __('signup.benefits_title') }}

@@ -157,7 +179,7 @@ class="position-absolute top-0 start-0 w-100 h-100 bg-body-secondary rounded-pill d-none d-block-dark">
-

Subscribe to your favorite products

+

{{ __('signup.benefit_1_title') }}

@@ -176,7 +198,7 @@ class="position-absolute top-0 start-0 w-100 h-100 bg-body-secondary rounded-pill d-none d-block-dark"> -

View and manage your orders and wishlist

+

{{ __('signup.benefit_2_title') }}

@@ -195,7 +217,7 @@ class="position-absolute top-0 start-0 w-100 h-100 bg-body-secondary rounded-pill d-none d-block-dark"> -

Earn rewards for future purchases

+

{{ __('signup.benefit_3_title') }}

@@ -214,7 +236,7 @@ class="position-absolute top-0 start-0 w-100 h-100 bg-body-secondary rounded-pill d-none d-block-dark"> -

Receive exclusive offers and discounts

+

{{ __('signup.benefit_4_title') }}

@@ -233,7 +255,7 @@ class="position-absolute top-0 start-0 w-100 h-100 bg-body-secondary rounded-pill d-none d-block-dark"> -

Create multiple wishlists

+

{{ __('signup.benefit_5_title') }}

@@ -252,7 +274,7 @@ class="position-absolute top-0 start-0 w-100 h-100 bg-body-secondary rounded-pill d-none d-block-dark"> -

Pay for purchases by installments

+

{{ __('signup.benefit_6_title') }}

diff --git a/resources/views/components/layout/header.blade.php b/resources/views/components/layout/header.blade.php index 43f6272..8a290e0 100644 --- a/resources/views/components/layout/header.blade.php +++ b/resources/views/components/layout/header.blade.php @@ -235,7 +235,7 @@ + href="{{ route('register') }}"> Account diff --git a/routes/web.php b/routes/web.php index c6e39fb..4bd60b9 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,5 +1,6 @@ name('produc Route::get('/search', [SearchController::class, 'search'])->name('search.ajax'); // Component loading routes -Route::get('/components/{component}', [ComponentController::class, 'load'])->name('component.load'); \ No newline at end of file +Route::get('/components/{component}', [ComponentController::class, 'load'])->name('component.load'); + +Route::get('/register', [RegisterController::class, 'index'])->name('register'); +Route::post('/register', [RegisterController::class, 'register'])->name('register');