diff --git a/app/Http/Controllers/Bo/CodeController.php b/app/Http/Controllers/Bo/CodeController.php index 9e373dd..6841b1d 100644 --- a/app/Http/Controllers/Bo/CodeController.php +++ b/app/Http/Controllers/Bo/CodeController.php @@ -28,7 +28,11 @@ class CodeController extends Controller /** @var \App\Models\Settings */ $settings = app(Settings::class); $nbCodeBase = Code::query()->count(); - $nbCodeUsed = Code::query()->where('code_used', true)->count(); + $nbCodeUsed = Code::query()->whereHas( + 'participations', + operator: '>', + count: $settings->nb_unique_code_use + )->count(); $nbCodeExported = Code::query()->where('code_exported', true)->count(); $nbCodeRemainingToExport = Code::query()->where('code_exported', false)->count(); return view('back::pages.codes.index', compact( diff --git a/app/Http/Controllers/Bo/ProductController.php b/app/Http/Controllers/Bo/ProductController.php index e5c2283..1956395 100644 --- a/app/Http/Controllers/Bo/ProductController.php +++ b/app/Http/Controllers/Bo/ProductController.php @@ -40,7 +40,7 @@ class ProductController extends Controller $search, null, 'name', - 'code_ean', + 'ean_code', ); } $searchFields = \sprintf( diff --git a/app/Http/Controllers/Fo/ParticipateController.php b/app/Http/Controllers/Fo/ParticipateController.php index eba0704..f9fe6a7 100644 --- a/app/Http/Controllers/Fo/ParticipateController.php +++ b/app/Http/Controllers/Fo/ParticipateController.php @@ -10,11 +10,13 @@ use App\Mail\NotifAdmin; use App\Mail\NotifWinAdmin; use App\Mail\PrizeWon; use App\Mail\DiscountWonMail; +use App\Mail\Won\PrizeWonAdminMail; use App\Models\Customer; use App\Models\InstantWin; use App\Models\Participation; use App\Models\Discount; use App\Models\Settings; +use App\Notifications\Customer\HasWonInstantWin; use Illuminate\Contracts\View\View; use Illuminate\Http\Request; use Illuminate\Support\Arr; @@ -40,6 +42,7 @@ class ParticipateController extends Controller public function __construct() { $this->middleware(function (Request $request, callable $next) { + $this->authorize('fo.gameEnabled'); $this->assertUserHasEnoughInfoToUseMesBr(); return $next($request); }); @@ -71,8 +74,8 @@ class ParticipateController extends Controller /** @var \App\Models\InstantWin|null */ $instantWin = InstantWin::query()->lockForUpdate() ->where('available_at', '<', now()) - ->where('won', false) - ->get()->sortBy('available_at', 'ASC')->first(); + ->whereDoesntHave('participation') + ->get()->sortBy('available_at')->first(); /** @var \App\Models\Settings */ $settings = app(Settings::class); @@ -81,9 +84,9 @@ class ParticipateController extends Controller $customer = auth('frontend')->user(); /** @var \App\Models\Code */ - $code = $request->post('code'); + $code = $request->post('unique_code'); /** @var \App\Models\Product|null */ - $product = $request->post('code'); + $product = $request->post('product'); // * Make new participation $participation = Participation::makeNew( @@ -94,6 +97,7 @@ class ParticipateController extends Controller ); // * Si l'utilisateur actuel à déjà gagné un gros lot, pas d'instant gagnant. + // ? As-t-on un instant gagnant ? if (Participation::customerAlreadyHasAnInstantWin($customer) or !$instantWin) { // ? Les BR sont activés ? if (!$settings->discounts_activated) { @@ -114,20 +118,28 @@ class ParticipateController extends Controller } // * Le joueur à remporté un instant gagnant - // Ici si fail car unique dupliqué catch. - try { - // Gain d'un gros lot. - $this->instantWin($request, $participation, $instantWin); - return view( - 'theme::pages.participation.won_prize', - compact('settings') - )->with('prize', $instantWin->prize); - } catch (\ErrorException $e) { - // Unbind instant gagnant. - $participation->instantWin()->delete(); - // phpcs:ignore - // TODO: si erreur il faut envoyer un mail à equipe.web@ldc.fr. - } //end try + $participation->instantWin()->save($instantWin); + $participation->save(); + + // * Notify customer for his win + $customer->notify(new HasWonInstantWin( + $instantWin->prize, + $customer->api_data->firstname + )); + + // * Dispatch notification to mail list + Mail::to($settings->notification_emails)->send(new PrizeWonAdminMail( + $settings->name, + $participation->created_at, + $instantWin->prize->label, + $customer->api_data, + $code->code_unique, + optional($product)->ean_code + )); + + return view('theme::pages.participation.won', [ + 'prizeModel' => $instantWin->prize + ]); }); } @@ -152,82 +164,6 @@ class ParticipateController extends Controller // * --- PRIVATE METHODS --- - /** - * Handle instant wins - * - * @param string $serial - * @param \App\Models\Participation $participation - * @param \App\Models\InstantWin|null $instantWin - * @return void - */ - private function instantWin( - string $serial, - Participation $participation, - InstantWin|null $instantWin - ): void { - /** @var \App\Models\Settings */ - $settings = app(Settings::class); - - $instantWin->won = true; - // Should save now to prevent next player win on the same winning moment. - $instantWin->save(); - $participation->instantWin()->save($instantWin); - // Envoi du courrier Arcadia. - if ( - $instantWin->prize->arcadia_operation_code !== 0 && - $instantWin->prize->arcadia_support_code !== 0 - ) { - $crmId = auth('backend')->user()->crm_id; - - $fields = [ - 'root' => [ - [ - 'fieldName' => 'order_date', - 'fieldValue' => date('d/m/Y') - ], - [ - 'fieldName' => 'operation', - 'fieldValue' => $instantWin->prize->arcadia_operation_code - ] - ], - 'orderLine' => [ - [ - 'fieldName' => 'code_unique', - 'fieldValue' => $request->serial - ], - [ - 'fieldName' => 'support_code', - 'fieldValue' => $instantWin->prize->arcadia_support_code - ], - [ - 'fieldName' => 'label_type_support', - 'fieldValue' => 'Gain du lot : ' . $instantWin->prize->label - ] - ] - ]; - - if ($settings->use_ean_codes === 1) { - $codeEan = $request->code_ean; - $fields['orderLine'][] = [ - 'fieldName' => 'code_on_pack', - 'fieldValue' => "$codeEan->code" - ]; - } - - $arcadiaMail = $this->apiCrm->ArcadiaOutgoingMail($crmId, $fields, 'game'); - if ($arcadiaMail->status === 'KO') { - Mail::to(config('mail.admin'))->send(new CourrierArcadiaError(auth('backend')->user())); - } - } //end if - - Mail::to(auth('backend')->user()->email)->send(new PrizeWon(auth('backend')->user(), $instantWin->prize, $settings)); - $mails = json_decode($settings->notification_emails); - foreach ($mails as $mail) { - Mail::to($mail->email)->send(new NotifWinAdmin($settings, $participation)); - } - $participation->save(); - } - /** * Détermine si un BR doit être servi * renvoi la vue de gain BR ou la vue "Perdu" @@ -319,11 +255,15 @@ class ParticipateController extends Controller } //end try // * Phase - 2 Mise à jour du coupon dans MesBr - /** @var \LDCWEB\MesBR */ - $mesBR = app('mesBrAPI'); + /** @var \LDC\MESBR\MesBr */ + $mesBr = app('mesBrAPI'); + + $mesBr->updateCustomer($crmUser->hashcode, $crmUser->email, $crmUser->country); + + $result = $mesBr->getMurCustomer($crmUser->user, $discount); + + $mesBR->updatePanier($crmUser->user, $discount); - $mesBR->updateCustomer($crmUser->user); - $result = $mesBR->updatePanier($crmUser->user, $discount); if ($result["status"] === "OK") { $discount = new Discount(); $discount->link = $result["link"]; @@ -353,7 +293,7 @@ class ParticipateController extends Controller */ private function assertUserHasEnoughInfoToUseMesBr() { - // * Valider que le client dispose des donnés requises pour MesBR + // * Valider que le client dispose des donnés requises pour MesBr try { /** @var \App\Models\Customer */ $authUser = auth('frontend')->user(); diff --git a/app/Http/Requests/Bo/Products/StoreProductRequest.php b/app/Http/Requests/Bo/Products/StoreProductRequest.php index 9240f40..6f8116e 100644 --- a/app/Http/Requests/Bo/Products/StoreProductRequest.php +++ b/app/Http/Requests/Bo/Products/StoreProductRequest.php @@ -41,10 +41,10 @@ class StoreProductRequest extends FormRequest { $rules = [ 'name' => 'required|string|min:3|max:255', - 'code_ean' => [ + 'ean_code' => [ 'required', 'numeric', - 'unique:products,code_ean', + 'unique:products,ean_code', 'regex:/^[0-9]{7}$|^[0-9]{8}$|^[0-9]{13}$/', ], 'picture_alt' => 'required_with:picture|string|between:3,255', @@ -83,7 +83,7 @@ class StoreProductRequest extends FormRequest 'model' => trans('models.classes.product'), ]), 'picture' => trans('validation.attributes.image'), - 'code_ean' => trans('validation.custom.ean_code'), + 'ean_code' => trans('validation.custom.ean_code'), 'range_id' => trans('models.classes.range'), ]; } diff --git a/app/Http/Requests/Bo/Products/UpdateProductRequest.php b/app/Http/Requests/Bo/Products/UpdateProductRequest.php index d103dd9..b33e69c 100644 --- a/app/Http/Requests/Bo/Products/UpdateProductRequest.php +++ b/app/Http/Requests/Bo/Products/UpdateProductRequest.php @@ -3,6 +3,7 @@ namespace App\Http\Requests\Bo\Products; use Illuminate\Support\Facades\Gate; +use Illuminate\Validation\Rule; class UpdateProductRequest extends StoreProductRequest { @@ -24,10 +25,10 @@ class UpdateProductRequest extends StoreProductRequest public function rules() { return array_merge(parent::rules(), [ - 'code_ean' => [ + 'ean_code' => [ 'required', 'numeric', - 'unique:products,code_ean,' . $this->route('product')->id . ',id', + Rule::unique('products', 'ean_code')->ignore($this->route('product')->id), 'regex:/^[0-9]{7}$|^[0-9]{8}$|^[0-9]{13}$/', ], ]); diff --git a/app/Http/Requests/Bo/Settings/UpdateSettingsRequest.php b/app/Http/Requests/Bo/Settings/UpdateSettingsRequest.php index fc212a5..ac0f893 100644 --- a/app/Http/Requests/Bo/Settings/UpdateSettingsRequest.php +++ b/app/Http/Requests/Bo/Settings/UpdateSettingsRequest.php @@ -66,6 +66,7 @@ class UpdateSettingsRequest extends FormRequest 'use_ean_codes' => 'required|boolean', 'activated_teaser' => 'required|boolean', 'activated_close_page' => 'required|boolean', + // phpcs:ignore Generic.Files.LineLength.TooLong 'close_page_active_date' => 'nullable|required_if:activated_close_page,true,1|date|after_or_equal:end_date', 'redirect_close' => 'required|url:http,https', diff --git a/app/Http/Requests/Fo/ParticipateRequest.php b/app/Http/Requests/Fo/ParticipateRequest.php index c4357c3..7dc2297 100644 --- a/app/Http/Requests/Fo/ParticipateRequest.php +++ b/app/Http/Requests/Fo/ParticipateRequest.php @@ -7,8 +7,10 @@ use App\Models\Participation; use App\Models\Product; use App\Models\Settings; use Illuminate\Contracts\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; +use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Str; use Kwaadpepper\Serial\Exceptions\SerialCasterException; @@ -19,8 +21,15 @@ class ParticipateRequest extends FormRequest * * @return boolean */ - public function authorize() + public function authorize(): bool { + /** @var \App\Models\Customer */ + $customer = auth('frontend')->user(); + // * Use rate limiter to prevent brute force sticker code + if (RateLimiter::tooManyAttempts("participate:{$customer->id}", $perMinute = 5)) { + return false; + } + RateLimiter::hit("participate:{$customer->id}"); return Gate::check('participate', Participation::class); } @@ -29,25 +38,51 @@ class ParticipateRequest extends FormRequest * * @return void */ - protected function prepareForValidation() + protected function prepareForValidation(): void { try { $this->merge([ - 'code' => Code::query()->where('id', Code::serialToInt( - Str::upper(\strval($this->code)) + 'unique_code' => Code::query()->where('id', Code::serialToInt( + Str::upper(\strval($this->input('unique_code'))) ))->first() ?? false, ]); } catch (SerialCasterException) { // * Provoquer l'affichage code incorrect. $this->merge([ - 'code' => false, + 'unique_code' => false, ]); } finally { $this->merge([ - 'code_ean' => (string)$this->string('code_ean')->padLeft(13, '0'), - 'accept_rules' => $this->boolean('accept_rules') + 'ean_code' => (string)$this->string('ean_code')->padLeft(13, '0'), + 'game_rules' => $this->boolean('game_rules') ]); } + + /** @var \App\Models\Settings */ + $settings = app(Settings::class); + + // ? Does the game use EAN Codes ? + if ($settings->use_ean_codes) { + /** + * * Check si les codes ean sont utilisés, auquel cas + * * on test celui envoyé pour savoir s'il est correct. + * Modif demande vincent : + * Tester code ean existe avec 13, puis 8 puis 7 chars + * Si le code EAN est trouvé dans la liste, alors on prend l'objet correspondant, + * sinon on prend le premier objet qui contient les 7 1er chiffres (cas des EAN pds) + * + * Dans la base chaque code ean doit être rentré dans sa valeur absolue + * càd que les code ean 8 sont de longueur 8, 7 de longueur7 et 13 de longueur 13. + */ + $this->merge([ + // * Union pour faire des recherches séparée en une seule requête. + // ! ean_code is already str_padded to 13 + 'product' => Product::query()->where('ean_code', $this->post('ean_code')) + ->union(Product::query()->where('ean_code', Str::limit($this->post('ean_code'), 8, ''))) + ->union(Product::query()->where('ean_code', Str::limit($this->post('ean_code'), 7, ''))) + ->first() + ]); + } //end if } /** @@ -55,41 +90,31 @@ class ParticipateRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { /** @var \App\Models\Settings */ $settings = app(Settings::class); $rules = [ - 'accept_rules' => 'required|boolean|accepted', - 'serial' => [ + 'game_rules' => 'required|boolean|accepted', + 'unique_code' => [ 'required', // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed - function ($attribute, $value, $fail) use ($settings) { + function (string $attribute, $value, callable $fail) use ($settings) { // ? Le Code unique est resolved ? if ($value === false or !\is_object($value) or \get_class($value) !== Code::class) { $fail(trans('Code unique incorrect, veuillez réessayer.')); return; } /** @var \App\Models\Code $value */ - // ? Code unique déjà trop utilisé ? - if ($value->code_used) { - $fail(trans('Ce code unique n\'est plus utilisable.')); - return; - } - - /** Request réutilisable */ - $participationQuery = Participation::query() - ->whereHas('code', fn (Builder $q) => $q->where('id', $value->id)); /** @var \App\Models\Customer */ $customer = auth('frontend')->user(); // * Check si l'utilisateur n'a pas déjà utilisé ce code. - $query = (clone $participationQuery)->whereHas( - 'customer', - fn (Builder $q) => $q->where('id', $customer->id) - ); + $query = Participation::query() + ->whereHas('code', fn (Builder $q) => $q->where('id', $value->id)) + ->whereHas('customer', fn (Builder $q) => $q->where('id', $customer->id)); // ! Une seule participation avec un code donné par joueur if ($query->exists()) { $fail(trans('Vous avez déjà utilisé ce code unique.')); @@ -98,40 +123,29 @@ class ParticipateRequest extends FormRequest // * Gestion du nombre d'utilisation d'un code. // ? Le code unique à été trop utilisé ? - if ((clone $participationQuery)->count() >= $settings->nb_unique_code_use) { - $value->code_used = true; - $value->saveOrFail(); - $fail(trans('Ce code unique n\'est plus utilisable.')); + if ($value->has_been_used_too_many_times) { + // * Ne pas laisser d'indices, message générique + $fail(trans('Code unique incorrect, veuillez réessayer.')); return; } } ] ]; - /** - * * Check si les codes ean sont utilisés, auquel cas - * * on test celui envoyé pour savoir s'il est correct. - * Modif demande vincent : - * Tester code ean existe avec 13, puis 8 puis 7 chars - * Si le code EAN est trouvé dans la liste, alors on prend l'objet correspondant, - * sinon on prend le premier objet qui contient les 7 1er chiffres (cas des EAN pds) - * - * Dans la base chaque code ean doit être rentré dans sa valeur absolue - * càd que les code ean 8 sont de longueur 8, 7 de longueur7 et 13 de longueur 13. - */ + // ? Does the game use EAN Codes ? if ($settings->use_ean_codes) { - $rules = \array_merge($rules, ['code_ean' => [ + $rules = \array_merge($rules, ['ean_code' => [ 'required', // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed function ($attribute, $value, $fail) { - // * Union pour faire des recherches séparée en une seule requête. - // ! code_ean is already str_padded to 13 - $productQuery = Product::query()->where('code_ean', $this->post('code_ean')) - ->union(Product::where('code_ean', Str::limit($this->post('code_ean'), 8, ''))) - ->union(Product::where('code_ean', Str::limit($this->post('code_ean'), 7, ''))); - - if (!$productQuery->exists()) { + // phpcs:enable + /** @var \App\Models\Produt|mixed */ + $product = $this->input('product'); + if ( + empty($product) or !is_object($product) or + !($product instanceof Model) or !$product->exists + ) { $fail(trans('Le code barre entré est incorrect, veuillez réessayer.')); } } @@ -145,12 +159,12 @@ class ParticipateRequest extends FormRequest * * @return array */ - public function attributes() + public function attributes(): array { return [ - 'code_ean' => \trans('code barre'), - 'serial' => \trans('code unique'), - 'accept_rules' => \trans('règlements du jeu'), + 'unique_code' => \trans('validation.custom.unique_code'), + 'ean_code' => \trans('validation.custom.ean_code'), + 'game_rules' => \trans('validation.custom.game_rules'), ]; } } diff --git a/app/Jobs/ProcessUniqueCode.php b/app/Jobs/ProcessUniqueCode.php index b6f2305..3997776 100644 --- a/app/Jobs/ProcessUniqueCode.php +++ b/app/Jobs/ProcessUniqueCode.php @@ -63,7 +63,7 @@ class ProcessUniqueCode implements ShouldQueue, ShouldBeUnique ->getColumnListing((new Code())->getTable())) ->filter(function ($name) { // * Ignorer les colonnes avec valeurs par défaut sur la table. - return !in_array($name, ['id', 'code_used', 'code_exported']); + return !in_array($name, ['id', 'code_exported']); })->count() + 1; /** diff --git a/app/Lib/vendor/LDCWEB/MesBR.php b/app/Lib/vendor/LDCWEB/MesBR.php deleted file mode 100644 index 5115481..0000000 --- a/app/Lib/vendor/LDCWEB/MesBR.php +++ /dev/null @@ -1,73 +0,0 @@ -endpoint = $endpoint; - $this->refCamp = $refCamp; - } - public function createCustomer(string $hashcode, string $email, string $country) - { - return $this->callAPI("POST", '/customer', ['hashcode' => $hashcode, 'email' => $email, 'country' => $country]); - } - public function updateCustomer(string $hashcode, string $email, string $country) - { - return $this->callAPI("POST", '/customer', ['hashcode' => $hashcode, 'email' => $email, 'country' => $country]); - } - public function getMurCustomer(string $hashcode) - { - return $this->callAPI("GET", '/customer/mur', ['refcamp' => $this->refCamp, 'hashcode' => $hashcode]); - } - public function getMur() - { - return $this->callAPI("GET", '/mur', ['refcamp' => $this->refCamp]); - } - public function printCoupons(string $hashcode, array $coupons) - { - return $this->callAPI("GET", '/coupons/print', ['refcamp' => $this->refCamp, 'hashcode' => $hashcode, 'coupons' => implode(",", $coupons)]); - } - private function callAPI($method, $url, $data = []) - { - $url = $this->endpoint . '/api/v1' . $url; - $curl = curl_init(); - switch ($method) { - case "POST": - curl_setopt($curl, CURLOPT_POST, 1); - if ($data) { - curl_setopt($curl, CURLOPT_POSTFIELDS, $data); - } - break; - case "PUT": - case "DELETE": - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); - if ($data) { - curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); - } - break; - default: - if ($data) { - $url = sprintf("%s?%s", $url, http_build_query($data)); - } - } - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_COOKIESESSION, true); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - - $result = curl_exec($curl); - if (!curl_errno($curl)) { - $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); - if (!in_array($http_code, [200, 201])) { - return (object)['status' => 'KO', 'code' => 500, 'message' => 'Connection to API failed - Code HTTP : ' . $http_code]; - } - } else { - return (object)['status' => 'KO', 'code' => 500, 'message' => 'Connection to API failed : ' . curl_error($curl)]; - } - curl_close($curl); - return json_decode($result); - } -} diff --git a/app/Lib/vendor/LDCWEB/MesBr.php b/app/Lib/vendor/LDCWEB/MesBr.php new file mode 100644 index 0000000..bafdb91 --- /dev/null +++ b/app/Lib/vendor/LDCWEB/MesBr.php @@ -0,0 +1,120 @@ +endpoint = $endpoint; + $this->refCamp = $refCamp; + } + + public function createCustomer(string $hashcode, string $email, string $country) + { + return $this->callApi("POST", '/customer', ['hashcode' => $hashcode, 'email' => $email, 'country' => $country]); + } + public function updateCustomer(string $hashcode, string $email, string $country) + { + return $this->callApi("POST", '/customer', ['hashcode' => $hashcode, 'email' => $email, 'country' => $country]); + } + public function getMurCustomer(string $hashcode) + { + return $this->callApi("GET", '/customer/mur', ['refcamp' => $this->refCamp, 'hashcode' => $hashcode]); + } + public function getMur() + { + return $this->callApi("GET", '/mur', ['refcamp' => $this->refCamp]); + } + public function printCoupons(string $hashcode, array $coupons) + { + return $this->callApi("GET", '/coupons/print', [ + 'refcamp' => $this->refCamp, 'hashcode' => $hashcode, 'coupons' => implode(",", $coupons) + ]); + } + private function callApi($method, $url, $data = []) + { + $url = $this->endpoint . '/api/v1' . $url; + $curl = curl_init(); + switch ($method) { + case "POST": + curl_setopt($curl, CURLOPT_POST, 1); + if ($data) { + curl_setopt($curl, CURLOPT_POSTFIELDS, $data); + } + break; + case "PUT": + case "DELETE": + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); + if ($data) { + curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); + } + break; + default: + if ($data) { + $url = sprintf("%s?%s", $url, http_build_query($data)); + } + } + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_COOKIESESSION, true); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + + $result = curl_exec($curl); + if (!curl_errno($curl)) { + $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + if (!in_array($http_code, [200, 201])) { + return (object)[ + 'status' => 'KO', 'code' => 500, 'message' => 'Connection to API failed - Code HTTP : ' . $http_code + ]; + } + } else { + return (object)[ + 'status' => 'KO', 'code' => 500, 'message' => 'Connection to API failed : ' . curl_error($curl) + ]; + } + curl_close($curl); + return json_decode($result); + } + + // * Added METHODS + + /** Update panier * + * @param $oCrmUser + * @param $couponId + * @param int $quantity + * @return array + */ + public function updatePanier($oCrmUser, $couponId, $quantity = 1) + { + $datas = [ + 'refsite' => $this->refSite, + 'refcamp' => $this->refCamp, + 'refadherent' => $oCrmUser->crm_id, + 'nom' => $oCrmUser->lastname, + 'prenom' => $oCrmUser->firstname, + 'email' => $oCrmUser->email, + 'code_postal' => $oCrmUser->postcode, + 'commune' => $oCrmUser->city, + 'pays' => $this->getCountry($oCrmUser->country), + 'panier' => "($quantity)$couponId" + ]; + + $res = $this->callAPI("GET", "/updatebasket", $datas); + + if (!$res) { + error_log("MESBR - CODE : 999 - MESSAGE : Cannot read API Response (non json)"); + return ['status' => 'KO', 'code' => 999, 'message' => 'Cannot read API Response (non json)']; + } + if ($res->Statut === 'KO') { + error_log("MESBR - CODE : $res->Code - MESSAGE : $res->Message"); + return ['status' => 'KO', 'code' => $res->Code, 'message' => $res->Message]; + } + $panierVerification = $this->endpoint . '/showbasket?basketid=' . $res->Panier->Panier; + $panier_link = $this->endpoint . '/?basketid=' . $res->Panier->Panier; + return ['status' => 'OK', 'link' => $panier_link, 'verif' => $panierVerification, 'basketId' => $res->Panier->Panier]; + } +} diff --git a/app/Mail/NotifAdmin.php b/app/Mail/NotifAdmin.php deleted file mode 100644 index 8bf4dcd..0000000 --- a/app/Mail/NotifAdmin.php +++ /dev/null @@ -1,41 +0,0 @@ -msg = $msg; - } - - /** - * Build the message. - * - * @return $this - */ - public function build() - { - /** @var \App\Models\Settings */ - $settings = app(Settings::class); - $msg = $this->msg; - return $this->subject("{$settings->name} : Erreur")->view('front.basic.notif-admin', compact('msg')); - } -} diff --git a/app/Mail/Won/DiscountWonMail.php b/app/Mail/Won/DiscountWonMail.php index a03a6c0..fef7817 100644 --- a/app/Mail/Won/DiscountWonMail.php +++ b/app/Mail/Won/DiscountWonMail.php @@ -10,6 +10,7 @@ use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\View; class DiscountWonMail extends Mailable implements ShouldQueue { @@ -56,6 +57,7 @@ class DiscountWonMail extends Mailable implements ShouldQueue */ public function content(): \Illuminate\Mail\Mailables\Content { + View::addNamespace('theme', resource_path('theme/mounted/views')); return new Content( markdown: 'theme::mails.won-discount', with: [ diff --git a/app/Mail/Won/PrizeWonAdminMail.php b/app/Mail/Won/PrizeWonAdminMail.php new file mode 100644 index 0000000..daab5cc --- /dev/null +++ b/app/Mail/Won/PrizeWonAdminMail.php @@ -0,0 +1,90 @@ + $this->game_name + ]), + ); + } + + /** + * Get the message content definition. + * + * @return \Illuminate\Mail\Mailables\Content + */ + public function content(): \Illuminate\Mail\Mailables\Content + { + $participation = [ + trans('Participation :'), + "Code Unique : {$this->unique_code}", + ]; + if ($this->ean_code) { + $participation[] = "Code EAN : {$this->ean_code}"; + } + return new Content( + htmlString: (new MailMessage()) + ->greeting(trans('Bonjour,')) + ->line(trans( + 'Le lot suivant a été gagné :prize_label le :date', + [ + 'prize_label' => $this->prize_label, + 'date' => $this->participation_date->isoFormat('LLLL') + ] + ))->lines([ + trans('Gagnant :'), + "{$this->customer->lastname} {$this->customer->firstname}", + "{$this->customer->address}", + "{$this->customer->address2}", + "{$this->customer->postcode} {$this->customer->city} - {$this->customer->country->label}", + "{$this->customer->email}", + "{$this->customer->phone}", + ])->lines($participation)->render() + ); + } +} diff --git a/app/Mail/Won/PrizeWonMail.php b/app/Mail/Won/PrizeWonMail.php index ac8fecd..d0d888d 100644 --- a/app/Mail/Won/PrizeWonMail.php +++ b/app/Mail/Won/PrizeWonMail.php @@ -10,6 +10,7 @@ use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\View; class PrizeWonMail extends Mailable implements ShouldQueue { @@ -56,6 +57,8 @@ class PrizeWonMail extends Mailable implements ShouldQueue */ public function content(): \Illuminate\Mail\Mailables\Content { + // * Fix for async usage + View::addNamespace('theme', resource_path('theme/mounted/views')); return new Content( markdown: 'theme::mails.won-prize', with: [ diff --git a/app/Models/Code.php b/app/Models/Code.php index 8773f32..dbad4b0 100644 --- a/app/Models/Code.php +++ b/app/Models/Code.php @@ -2,18 +2,20 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Cache; use Kwaadpepper\Serial\SerialCaster; /** * Code. * * @property string $code_unique The unique code matching this code id. - * @property boolean $code_used Is the code used too much ? * @property boolean $code_exported Is the code exported ? * @property \Illuminate\Support\Carbon $date_creation Code creation date. * - * @property-read string|null $code_unique The unique code + * @property-read string|null $code_unique The unique code + * @property-read boolean $has_been_used_too_many_times Has this code been used too many times ? * * @method public static function intToSerial($int) Generate a serial from an int. * @method public static function serialToInt($serial) Get an int from a serial. @@ -36,7 +38,6 @@ class Code extends Model * @var array */ protected $fillable = [ - 'code_used', 'code_exported', 'date_creation' ]; @@ -44,10 +45,9 @@ class Code extends Model /** * The attributes that should be cast. * - * @var array + * @var array */ protected $casts = [ - 'code_used' => 'bool', 'code_exported' => 'bool', 'date_creation' => 'date', ]; @@ -59,6 +59,20 @@ class Code extends Model */ public $timestamps = false; + // * EVENTS + + /** + * Perform any actions required after the model boots. + * + * @return void + */ + protected static function booted() + { + static::created(fn () => Cache::forget('codes.count')); + static::updated(fn () => Cache::forget('codes.count')); + static::deleted(fn () => Cache::forget('codes.count')); + } + // * RELATIONS /** @@ -71,18 +85,23 @@ class Code extends Model return $this->hasMany(Participation::class, 'code_unique_id'); } - // * FUNCTIONS + // * METHODS + /** * Generate code_unique attribute * * @return string|null */ - public function getCodeUniqueAttribute() + public function codeUnique(): Attribute { - if (!$this->id or !\is_integer($this->id) or $this->id < 1) { - return null; - } - return self::intToSerial($this->id); + return Attribute::make( + get: function (): ?string { + if (!$this->id or !\is_integer($this->id) or $this->id < 1) { + return null; + } + return self::intToSerial($this->id); + } + ); } /** @@ -127,4 +146,26 @@ class Code extends Model config('codes.codeCharList') ); } + + // * GETTERS + + /** + * Get has_been_used_too_namy_times attribute + * + * @return \Illuminate\Database\Eloquent\Casts\Attribute + */ + public function hasBeenUsedTooManyTimes(): Attribute + { + /** @var \App\Models\Settings */ + $settings = app(Settings::class); + return Attribute::make( + get: fn (): bool => self::query() + ->where('id', $this->id) + ->whereHas( + 'participations', + operator: '>', + count: $settings->nb_unique_code_use + )->exists() + ); + } } diff --git a/app/Models/Customer.php b/app/Models/Customer.php index bea08e4..41b3fd8 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -8,6 +8,7 @@ use App\Notifications\Customer\ResetPassword; use App\Notifications\Customer\VerifyEmail; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Contracts\Database\Query\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Prunable; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\User as Authenticatable; @@ -56,6 +57,8 @@ class Customer extends Authenticatable implements MustVerifyEmail 'email_verified_at' => 'datetime', ]; + // * EVENTS + /** * Perform any actions required after the model boots. * @@ -71,7 +74,7 @@ class Customer extends Authenticatable implements MustVerifyEmail static::assertEmailIsUnique($model->id, $model->email); }); static::deleting(function () { - throw new \RuntimeException('This cannot be done à cause de MesBR'); + throw new \RuntimeException('This cannot be done à cause de MesBr'); }); static::deleted(function (self $model) { if ($model->forceDeleting) { @@ -241,22 +244,23 @@ class Customer extends Authenticatable implements MustVerifyEmail // * --- END AUTH --- /** - * Get the crm Data of a customer + * Get api_data attribute, the crm Data of a customer * - * @return \LDC\LDCCRM\Models\Customer - * @throws \LDC\LDCCRM\Exceptions\CrmException If cannot fetch data from crm. + * @return \Illuminate\Database\Eloquent\Casts\Attribute */ - public function getApiDataAttribute(): CrmCustomer + public function apiData(): Attribute { - return self::cacheCrm($this->hashcode, "apiData", function () { - /** @var \LDC\LDCCRM\CrmApiV2 */ - $crm = app('crmAPI'); - $crmCustomer = $crm->getCustomer($this->hashcode); - if (!$crmCustomer) { - throw new CrmException('CRM get Customer returned null'); - } - return $crmCustomer; - }); + return Attribute::make( + get: fn (): CrmCustomer => self::cacheCrm($this->hashcode, "apiData", function () { + /** @var \LDC\LDCCRM\CrmApiV2 */ + $crm = app('crmAPI'); + $crmCustomer = $crm->getCustomer($this->hashcode); + if (!$crmCustomer) { + throw new CrmException('CRM get Customer returned null'); + } + return $crmCustomer; + }) + ); } /** diff --git a/app/Models/FaqQuestion.php b/app/Models/FaqQuestion.php index 9bbed74..589628c 100644 --- a/app/Models/FaqQuestion.php +++ b/app/Models/FaqQuestion.php @@ -39,6 +39,8 @@ class FaqQuestion extends Model */ protected $with = ['theme']; + // * EVENTS + /** * Perform any actions required after the model boots. * diff --git a/app/Models/FaqTheme.php b/app/Models/FaqTheme.php index bbcbe78..35209c8 100644 --- a/app/Models/FaqTheme.php +++ b/app/Models/FaqTheme.php @@ -30,6 +30,8 @@ class FaqTheme extends Model */ protected $fillable = ['name']; + // * EVENTS + /** * Perform any actions required after the model boots. * diff --git a/app/Models/InstantWin.php b/app/Models/InstantWin.php index eb41644..4df43d7 100644 --- a/app/Models/InstantWin.php +++ b/app/Models/InstantWin.php @@ -2,19 +2,21 @@ namespace App\Models; +use App\Traits\Models\IsCachedAsCollection; use Illuminate\Database\Eloquent\Model; /** * InstantWin. * * @property \Illuminate\Support\Carbon $available_at When the instant win will be available. - * @property boolean $won Is the prize won ? * * @property-read \App\Models\Prize $prize Associated prize. * @property-read \App\Models\Participation $participation Associated participation. */ class InstantWin extends Model { + use IsCachedAsCollection; + /** * Indicates if the model should be timestamped. * @@ -29,14 +31,19 @@ class InstantWin extends Model */ protected $with = ['prize']; + // * EVENTS + /** - * The attributes that should be cast to native types. + * Perform any actions required after the model boots. * - * @var array + * @return void */ - protected $casts = [ - 'won' => 'bool' - ]; + protected static function booted() + { + static::creating(fn () => self::removeCached()); + static::updating(fn () => self::removeCached()); + static::deleting(fn () => self::removeCached()); + } // * RELATIONSHIPS diff --git a/app/Models/Participation.php b/app/Models/Participation.php index af0d109..8a0862e 100644 --- a/app/Models/Participation.php +++ b/app/Models/Participation.php @@ -17,7 +17,7 @@ use IPLib\Address\AddressInterface; * @property-read \Illuminate\Support\Carbon $created_at Creation Date. * @property-read \Illuminate\Support\Carbon $updated_at Last update Date. * - * @property-read string|null $code_ean Accessor for code_ean, from product table. + * @property-read string|null $ean_code Accessor for ean_code, from product table. * * @property-read \App\Models\Customer $customer Associated customer. * @property-read \App\Models\Code $code Associated code. @@ -82,14 +82,14 @@ class Participation extends Model // * GETTERS /** - * Accessor for code_ean, from product table. + * Accessor for ean_code, from product table. * * @return \Illuminate\Database\Eloquent\Casts\Attribute; */ - public function codeEan(): Attribute + public function eanCode(): Attribute { return Attribute::make( - get: fn () => optional($this->product)->code_ean, + get: fn () => optional($this->product)->ean_code, ); } @@ -129,7 +129,7 @@ class Participation extends Model public static function customerAlreadyHasAnInstantWin(Customer $customer): bool { return self::query()->whereHas('instantWin') - ->whereHas('user', fn (Builder $q) => $q->where('id', $customer->id)) + ->whereHas('customer', fn (Builder $q) => $q->where('id', $customer->id)) ->exists(); } } diff --git a/app/Models/Prize.php b/app/Models/Prize.php index b63ce90..3527b36 100644 --- a/app/Models/Prize.php +++ b/app/Models/Prize.php @@ -48,6 +48,8 @@ class Prize extends Model 'pdf_winning_proof' ]; + // * EVENTS + /** * Perform any actions required after the model boots. * diff --git a/app/Models/Product.php b/app/Models/Product.php index 6a5ed54..e8b890c 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -14,7 +14,7 @@ use Illuminate\Database\Eloquent\Model; * @property string $picture Path of the picture * @property string $picture_alt Alt text for the picture * @property string $picture_title Title text for the picture - * @property integer $code_ean Ean code + * @property integer $ean_code Ean code * @property-read \Illuminate\Support\Carbon $created_at Creation Date. * @property-read \Illuminate\Support\Carbon $updated_at Last update Date. * @@ -37,7 +37,7 @@ class Product extends Model 'picture', 'picture_alt', 'picture_title', - 'code_ean', + 'ean_code', 'range_id', ]; diff --git a/app/Models/Settings.php b/app/Models/Settings.php index a982c37..9251671 100644 --- a/app/Models/Settings.php +++ b/app/Models/Settings.php @@ -6,7 +6,10 @@ use App\Casts\AsArrayOfDiscount; use App\Lib\Discount; use App\Lib\Helpers\FileStorageHelper; use App\Traits\Models\IsCachedAsCollection; +use Carbon\CarbonInterval; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Cache; /** * Settings. @@ -87,6 +90,8 @@ class Settings extends Model 'notification_emails' => 'array', ]; + // * EVENTS + /** * Perform any actions required after the model boots. * @@ -127,6 +132,46 @@ class Settings extends Model return FileStorageHelper::onlyUploadedFilesWithFileStorageHelper(collect($files)->filter()->all()); } + /** + * Check if the game can be play and report if it cannot. + * + * Demande Vincent: Methods to check via policy if game can be played + * + * Besoin: + * - Min 1 code unique + * - Min 1 Instant gagnant + * - Min 1 Product si Settings use_ean_codes + * - Min 1 WebCoupon si Settings discounts_activated + * + * @return boolean + */ + public function checkGameCanBePlayed(): bool + { + /** @var \App\Models\Settings */ + $settings = app(Settings::class); + if (!(bool)Prize::getCached()->count()) { + report(new \RuntimeException('Need prizes to enable the game')); + return false; + } + if (!(bool)Cache::remember('codes.count', CarbonInterval::day(), fn () => Code::query()->count())) { + report(new \RuntimeException('Need codes to enable the game')); + return false; + } + if (!(bool)InstantWin::getCached()->count()) { + report(new \RuntimeException('Need instant win to enable the game')); + return false; + } + if ($settings->use_ean_codes and !(bool)Product::getCached()->count()) { + report(new \RuntimeException('Need products to enable the game using ean codes')); + return false; + } + if ($settings->discounts_activated and !(bool)collect($this->web_coupons)->count()) { + report(new \RuntimeException('Need MesBr coupons to enable the game using discounts')); + return false; + } + return true; + } + // * GETTERS /** @@ -134,11 +179,13 @@ class Settings extends Model * * The game has started ? * - * @return boolean + * @return \Illuminate\Database\Eloquent\Casts\Attribute */ - public function getGameHasBegunAttribute(): bool + public function gameHasBegun(): Attribute { - return $this->start_date->lte(now()); + return Attribute::make( + get: fn (): bool => $this->start_date->lte(now()) + ); } /** @@ -146,11 +193,13 @@ class Settings extends Model * * The game has ended ? * - * @return boolean + * @return \Illuminate\Database\Eloquent\Casts\Attribute */ - public function getGameHasEndedAttribute(): bool + public function gameHasEnded(): Attribute { - return $this->game_has_begun and $this->end_date->lte(now()); + return Attribute::make( + get: fn (): bool => $this->game_has_begun and $this->end_date->lte(now()) + ); } /** @@ -158,11 +207,13 @@ class Settings extends Model * * The game is oppen ? * - * @return boolean + * @return \Illuminate\Database\Eloquent\Casts\Attribute */ - public function getGameIsOpenAttribute(): bool + public function gameIsOpen(): Attribute { - return $this->game_has_begun and !$this->game_has_ended; + return Attribute::make( + get: fn (): bool => $this->game_has_begun and !$this->game_has_ended + ); } /** @@ -170,13 +221,15 @@ class Settings extends Model * * The teaser page has to be displayed ? * - * @return boolean + * @return \Illuminate\Database\Eloquent\Casts\Attribute */ - public function getShouldDisplayTeaserPageAttribute(): bool + public function shouldDisplayTeaserPage(): Attribute { - return !$this->game_has_begun and - !$this->game_has_ended and - $this->activated_teaser; + return Attribute::make( + get: fn (): bool => !$this->game_has_begun and + !$this->game_has_ended and + $this->activated_teaser + ); } /** @@ -184,13 +237,15 @@ class Settings extends Model * * The end page has to be displayed ? * - * @return boolean + * @return \Illuminate\Database\Eloquent\Casts\Attribute */ - public function getShouldDisplayEndPageAttribute(): bool + public function shouldDisplayEndPage(): Attribute { - return $this->game_has_begun and - $this->game_has_ended and - $this->activated_close_page and - $this->close_page_active_date->gte(now()); + return Attribute::make( + get: fn (): bool => $this->game_has_begun and + $this->game_has_ended and + $this->activated_close_page and + $this->close_page_active_date->gte(now()) + ); } } diff --git a/app/Models/StaticPage.php b/app/Models/StaticPage.php index 1639079..2255d7c 100644 --- a/app/Models/StaticPage.php +++ b/app/Models/StaticPage.php @@ -45,6 +45,8 @@ class StaticPage extends Model 'type' => StaticPageType::class, ]; + // * EVENTS + /** * Perform any actions required after the model boots. * diff --git a/app/Models/User.php b/app/Models/User.php index 91ae6f9..a31dd48 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -4,6 +4,7 @@ namespace App\Models; use App\Enums\Users\RoleEnum; use App\Traits\Models\ActivityLog; +use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -64,6 +65,8 @@ class User extends Authenticatable 'published' => 'boolean', ]; + // * EVENTS + /** * Perform any actions required after the model boots. * diff --git a/app/Notifications/Customer/DestroyCustomer.php b/app/Notifications/Customer/DestroyCustomer.php index 50bbc66..eff184d 100644 --- a/app/Notifications/Customer/DestroyCustomer.php +++ b/app/Notifications/Customer/DestroyCustomer.php @@ -2,6 +2,7 @@ namespace App\Notifications\Customer; +use App\Models\Settings; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; diff --git a/app/Notifications/Customer/DestroyedCustomer.php b/app/Notifications/Customer/DestroyedCustomer.php index 5d5de4f..a4035c0 100644 --- a/app/Notifications/Customer/DestroyedCustomer.php +++ b/app/Notifications/Customer/DestroyedCustomer.php @@ -2,6 +2,7 @@ namespace App\Notifications\Customer; +use App\Models\Settings; use Illuminate\Notifications\Notification; use Illuminate\Notifications\Messages\MailMessage; diff --git a/app/Notifications/Customer/HasWonInstantWin.php b/app/Notifications/Customer/HasWonInstantWin.php new file mode 100644 index 0000000..adc3a05 --- /dev/null +++ b/app/Notifications/Customer/HasWonInstantWin.php @@ -0,0 +1,63 @@ + + * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass + */ + public function via(object $notifiable): array + { + // phpcs:enable + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param \App\Models\Customer $notifiable + * @return \Illuminate\Contracts\Mail\Mailable + */ + public function toMail(Customer $notifiable): Mailable + { + /** @var \App\Models\Settings */ + $settings = app(Settings::class); + return (new PrizeWonMail( + new Address( + $notifiable->email, + $this->first_name + ), + $settings->name, + $this->prize, + config('app.url') + )); + } +} diff --git a/app/Notifications/Customer/ResetPassword.php b/app/Notifications/Customer/ResetPassword.php index 54a317d..8ae445e 100644 --- a/app/Notifications/Customer/ResetPassword.php +++ b/app/Notifications/Customer/ResetPassword.php @@ -2,6 +2,7 @@ namespace App\Notifications\Customer; +use App\Models\Settings; use Illuminate\Auth\Notifications\ResetPassword as NotificationsResetPassword; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Support\Facades\URL; diff --git a/app/Notifications/Customer/UpdateEmail.php b/app/Notifications/Customer/UpdateEmail.php index 6845782..8163027 100644 --- a/app/Notifications/Customer/UpdateEmail.php +++ b/app/Notifications/Customer/UpdateEmail.php @@ -2,6 +2,7 @@ namespace App\Notifications\Customer; +use App\Models\Settings; use Illuminate\Notifications\Notification; use Illuminate\Notifications\Messages\MailMessage; diff --git a/app/Notifications/Customer/UpdatePassword.php b/app/Notifications/Customer/UpdatePassword.php index b382ef1..23a20c5 100644 --- a/app/Notifications/Customer/UpdatePassword.php +++ b/app/Notifications/Customer/UpdatePassword.php @@ -2,6 +2,7 @@ namespace App\Notifications\Customer; +use App\Models\Settings; use Illuminate\Notifications\Notification; use Illuminate\Notifications\Messages\MailMessage; diff --git a/app/Notifications/Customer/VerifyEmail.php b/app/Notifications/Customer/VerifyEmail.php index e081bbd..c17eb6f 100644 --- a/app/Notifications/Customer/VerifyEmail.php +++ b/app/Notifications/Customer/VerifyEmail.php @@ -2,6 +2,7 @@ namespace App\Notifications\Customer; +use App\Models\Settings; use Illuminate\Auth\Notifications\VerifyEmail as NotificationsVerifyEmail; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Support\Carbon; diff --git a/app/Notifications/InstantWin/HasBennWon.php b/app/Notifications/InstantWin/HasBennWon.php new file mode 100644 index 0000000..4581174 --- /dev/null +++ b/app/Notifications/InstantWin/HasBennWon.php @@ -0,0 +1,54 @@ + + */ + public function via(object $notifiable): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(object $notifiable): MailMessage + { + return (new MailMessage) + ->line('The introduction to the notification.') + ->action('Notification Action', url('/')) + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray(object $notifiable): array + { + return [ + // + ]; + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 6065f2f..f07f12c 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -7,6 +7,7 @@ use App\Enums\Users\RoleEnum; use App\Models\Customer; use App\Models\FaqQuestion; use App\Models\Product; +use App\Models\Settings; use App\Models\User; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Gate; @@ -53,6 +54,16 @@ class AuthServiceProvider extends ServiceProvider (bool)FaqQuestion::getCached()->count() ); + Gate::define( + 'fo.gameEnabled', + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass + function (User|Customer|null $user) { + /** @var \App\Models\Settings */ + $settings = app(Settings::class); + return $settings->checkGameCanBePlayed(); + } + ); + $this->app->auth->provider('crm', function ($app, array $config) { return new CrmUserProvider($app['hash'], $config['model']); }); diff --git a/app/Providers/MesBrApiServiceProvider.php b/app/Providers/MesBrApiServiceProvider.php index bbc644b..38b9d16 100644 --- a/app/Providers/MesBrApiServiceProvider.php +++ b/app/Providers/MesBrApiServiceProvider.php @@ -5,7 +5,7 @@ namespace App\Providers; use App\Models\Settings; use Illuminate\Support\Facades\App; use Illuminate\Support\ServiceProvider; -use LDCWEB\MesBR; +use LDC\MESBR\MesBr; class MesBrApiServiceProvider extends ServiceProvider { @@ -20,7 +20,7 @@ class MesBrApiServiceProvider extends ServiceProvider /** @var \App\Models\Settings|null */ $settings = app(Settings::class); - return new MesBR( + return new MesBr( config('mesbr.endpoint'), $settings->discounts_site, $settings->discounts_campaign diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 5fee934..438e4db 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -79,9 +79,9 @@ class RouteServiceProvider extends ServiceProvider } // * Flatten if needed $cspReport = collect(request()->json('csp-report')) - ->flatMap(function ($v, $k) { - return [$k => is_array($v) ? collect($v)->flatten()->implode(', ') : $v]; - })->all(); + ->flatMap(function ($v, $k) { + return [$k => is_array($v) ? collect($v)->flatten()->implode(', ') : $v]; + })->all(); // * Filter array keys $cspReport = collect(array_intersect_key($cspReport, [ 'blocked-uri' => '', 'document-uri' => '', 'original-policy' => '', @@ -93,7 +93,7 @@ class RouteServiceProvider extends ServiceProvider $cspReport->implode("\n") )); })->middleware('throttle:5,2') - ->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class) + ->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class) ->name('cspReportUri'); } } diff --git a/composer.json b/composer.json index 9edff3a..714dbf1 100644 --- a/composer.json +++ b/composer.json @@ -65,7 +65,7 @@ "autoload-dev": { "psr-4": { "Tests\\": "tests/", - "LDCWEB\\": "app/Lib/vendor/LDCWEB" + "LDC\\MESBR\\": "app/Lib/vendor/LDCWEB" } }, "scripts": { diff --git a/database/factories/ProductFactory.php b/database/factories/ProductFactory.php index eace3a9..c46ae31 100644 --- a/database/factories/ProductFactory.php +++ b/database/factories/ProductFactory.php @@ -19,7 +19,7 @@ class ProductFactory extends Factory return [ 'name' => 'product ' . \fake()->jobTitle(), // * EAN 13 generate - 'code_ean' => fake()->randomNumber(8, true) . fake()->randomNumber(5, true), + 'ean_code' => fake()->randomNumber(8, true) . fake()->randomNumber(5, true), 'picture_alt' => fake()->sentence(), 'picture_title' => fake()->sentence(), ]; diff --git a/database/migrations/2020_10_20_165858_create_codes.php b/database/migrations/2020_10_20_165858_create_codes.php index e9c909d..dfd96d1 100644 --- a/database/migrations/2020_10_20_165858_create_codes.php +++ b/database/migrations/2020_10_20_165858_create_codes.php @@ -15,7 +15,6 @@ return new class extends Migration { Schema::create('codes', function (Blueprint $table) { $table->unsignedBigInteger('id')->primary(); - $table->boolean('code_used')->default(0)->comment('Is the code used too much ?'); $table->boolean('code_exported')->default(0)->comment('Has the code been exported ?'); $table->date('date_creation')->comment('Code creation date'); }); diff --git a/database/migrations/2021_08_18_162010_create_products_table.php b/database/migrations/2021_08_18_162010_create_products_table.php index 1e3b146..c7c4653 100644 --- a/database/migrations/2021_08_18_162010_create_products_table.php +++ b/database/migrations/2021_08_18_162010_create_products_table.php @@ -20,7 +20,7 @@ return new class extends Migration $table->string('picture_alt')->nullable()->comment('Attribut alt de l\'image du produit'); $table->string('picture_title')->nullable()->comment('Attribut title de l\'image du produit'); - $table->unsignedBigInteger('code_ean')->comment('Code EAN du produit pour la participation au jeu'); + $table->unsignedBigInteger('ean_code')->comment('Code EAN du produit pour la participation au jeu'); $table->foreignId('range_id')->before('created_at')->references('id') ->on('ranges')->onDelete('cascade'); diff --git a/database/migrations/2023_08_22_120243_create_instant_wins.php b/database/migrations/2023_08_22_120243_create_instant_wins.php index e8aec91..3d0f349 100644 --- a/database/migrations/2023_08_22_120243_create_instant_wins.php +++ b/database/migrations/2023_08_22_120243_create_instant_wins.php @@ -17,7 +17,6 @@ return new class extends Migration $table->id(); $table->timestamp('available_at') ->comment('La date-heure à laquelle l\'instant gagnant pourras être gagné'); - $table->boolean('won')->default(0)->comment('L\'instant à été gagné ?'); $table->foreignId('prize_id')->comment('Le lot qui à été gagné')->constrained('prizes'); $table->foreignId('participation_id') diff --git a/database/seeders/Development/PrizeSeeder.php b/database/seeders/Development/PrizeSeeder.php index 2a9fa59..47e465f 100644 --- a/database/seeders/Development/PrizeSeeder.php +++ b/database/seeders/Development/PrizeSeeder.php @@ -4,6 +4,7 @@ namespace Database\Seeders\Development; use App\Lib\Helpers\FileStorageHelper; use App\Models\Prize; +use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\File; @@ -11,6 +12,8 @@ use Illuminate\Support\Facades\Schema; class PrizeSeeder extends Seeder { + use WithoutModelEvents; + /** * Run the database seeds. * diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index 7515696..07390c6 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -238,6 +238,7 @@ return [ 'ip' => 'IP', 'code' => 'code', + 'unique_code' => 'code unique', 'product' => 'produit', 'value' => 'valeur', 'question' => 'question', @@ -268,5 +269,6 @@ return [ 'discounts_site' => 'référence du site', 'discounts_campaign' => 'référence de la campagne', 'analytics' => 'analytics', + 'game_rules' => 'règlement du jeu', ], ]; diff --git a/resources/views/layouts/head.blade.php b/resources/views/layouts/head.blade.php index 38da78f..8a9e880 100644 --- a/resources/views/layouts/head.blade.php +++ b/resources/views/layouts/head.blade.php @@ -1,6 +1,5 @@ {{-- Meta --}} - {{-- blade-formatter-disable --}} @hasSection('metaIndex') diff --git a/resources/views/pages/codes/search.blade.php b/resources/views/pages/codes/search.blade.php index 4922dc1..fd1687b 100644 --- a/resources/views/pages/codes/search.blade.php +++ b/resources/views/pages/codes/search.blade.php @@ -18,9 +18,9 @@
{{ __('Voici l\'état du code trouvé') }}
{{ __('Code') }} : {{ $code->code_unique }}
{{ __('ID') }} : {{ $code->id }}
-
{{ __('Utilisé ?') }} : +
{{ __('À atteint son nombre maximal d\'utilisation ?') }} : - @if ($code->code_used) + @if ($code->has_been_used_too_many_times) {{ Str::ucfirst(__('oui')) }} @else {{ Str::ucfirst(__('non')) }} diff --git a/resources/views/pages/dashboard/index.blade.php b/resources/views/pages/dashboard/index.blade.php index 156afd5..5fb097d 100644 --- a/resources/views/pages/dashboard/index.blade.php +++ b/resources/views/pages/dashboard/index.blade.php @@ -17,7 +17,7 @@ {{ __('Top 3 des codes EAN les plus utilisés') }} :
    @foreach ($codesEan as $codeEan) -
  • {{ $codeEan->code_ean }} : {{ $codeEan->nb_usage }}
  • +
  • {{ $codeEan->ean_code }} : {{ $codeEan->nb_usage }}
  • @endforeach
@endif diff --git a/resources/views/pages/products/form-inputs.blade.php b/resources/views/pages/products/form-inputs.blade.php index e1cffd3..d1d07d2 100644 --- a/resources/views/pages/products/form-inputs.blade.php +++ b/resources/views/pages/products/form-inputs.blade.php @@ -104,7 +104,7 @@ - {{-- CODE_EAN --}} + {{-- EAN_CODE --}}
{{ Str::ucfirst(__('validation.custom.ean_code')) }} @@ -115,7 +115,7 @@ 'label' => __(':field', [ 'field' => Str::ucfirst(__('validation.custom.ean_code')), ]), - 'name' => 'code_ean', + 'name' => 'ean_code', 'required' => true, 'model' => $productModel ?? null, 'helper' => __('validation.ean', [ diff --git a/resources/views/pages/products/index.blade.php b/resources/views/pages/products/index.blade.php index 56e2061..5800d72 100644 --- a/resources/views/pages/products/index.blade.php +++ b/resources/views/pages/products/index.blade.php @@ -20,7 +20,7 @@ @include('back::modules.table-col-sorter', [ 'cols' => [ 'name' => Str::ucfirst(__('validation.attributes.name')), - 'code_ean' => Str::ucfirst(__('validation.custom.ean_code')), + 'ean_code' => Str::ucfirst(__('validation.custom.ean_code')), 'range_id' => Str::ucfirst(__('models.classes.range')), ], ]) @@ -29,7 +29,7 @@ @foreach ($productModels as $productModel) {{ $productModel->name }} - {{ $productModel->code_ean }} + {{ $productModel->ean_code }} {{ $productModel->range->name }}
{{ Str::ucfirst(__('validation.custom.ean_code')) }} - {{ $productModel->code_ean }} + {{ $productModel->ean_code }}
{{ Str::ucfirst(__('models.classes.range')) }} diff --git a/resources/views/pages/winners-list/index.blade.php b/resources/views/pages/winners-list/index.blade.php index 4c59d97..1d5e41d 100644 --- a/resources/views/pages/winners-list/index.blade.php +++ b/resources/views/pages/winners-list/index.blade.php @@ -17,7 +17,7 @@ 'email' => __('Email'), ]; if ($settings->use_ean_codes === 1) { - $cols['code_ean_id'] = __('Code EAN'); + $cols['ean_code_id'] = __('Code EAN'); } $cols['code_unique_id'] = __('Code unique'); if ($settings->winning_proof === 1) { diff --git a/resources/views/partials/inputs/tinyint-boolean.blade.php b/resources/views/partials/inputs/tinyint-boolean.blade.php index 124fc36..1c1e537 100644 --- a/resources/views/partials/inputs/tinyint-boolean.blade.php +++ b/resources/views/partials/inputs/tinyint-boolean.blade.php @@ -10,7 +10,7 @@ --}}
- +