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 @@