feat: ✨ update codes types (unique and ean) #43

This commit is contained in:
Alexis Gousseau 2025-08-14 14:12:13 +00:00 • committed by Vincent BRIDIER
parent 2339684802
commit 1a4d0a03ec
41 changed files with 383 additions and 239 deletions

25
app/Enums/CodeType.php Normal file
View file

@ -0,0 +1,25 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum CodeType: int
{
use BaseEnum;
case unique = 1;
case ean = 2;
case both = 3;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::unique->name => 'Codes uniques',
self::ean->name => 'Codes EAN',
self::both->name => 'Codes uniques et codes EAN'
];
}

View file

@ -27,7 +27,7 @@ class ParticipationController extends Controller
$this->authorize('viewWinnerList', Participation::class); $this->authorize('viewWinnerList', Participation::class);
/** @var \Illuminate\Database\Eloquent\Builder $participationModels */ /** @var \Illuminate\Database\Eloquent\Builder $participationModels */
$participationModels = Participation::query()->with(['customer', 'code', 'instantWin'])->whereHas('instantWin'); $participationModels = Participation::query()->with(['customer', 'code', 'instantWin', 'product'])->whereHas('instantWin');
/** Sort columns with a query */ /** Sort columns with a query */
$this->sortQuery($participationModels); $this->sortQuery($participationModels);

View file

@ -37,7 +37,7 @@ class Controller extends BaseController
protected function getRandomWeightedElementIndex(array $weightedValues): int protected function getRandomWeightedElementIndex(array $weightedValues): int
{ {
mt_srand(time()); mt_srand(time());
$rand = mt_rand(1, max((int)collect($weightedValues)->sum('percentage'), 1)); $rand = mt_rand(1, max((int) collect($weightedValues)->sum('percentage'), 1));
foreach ($weightedValues as $key => $value) { foreach ($weightedValues as $key => $value) {
$rand -= $value->percentage; $rand -= $value->percentage;

View file

@ -60,11 +60,11 @@ class VerificationController extends Controller
/** @var \App\Models\Customer */ /** @var \App\Models\Customer */
$user = $request->user('frontend'); $user = $request->user('frontend');
if (!hash_equals((string)$request->route('id'), (string)$user->getKey())) { if (!hash_equals((string) $request->route('id'), (string) $user->getKey())) {
throw new AuthorizationException(); throw new AuthorizationException();
} }
if (!hash_equals((string)$request->route('hash'), sha1($user->getEmailForVerification()))) { if (!hash_equals((string) $request->route('hash'), sha1($user->getEmailForVerification()))) {
throw new AuthorizationException(); throw new AuthorizationException();
} }

View file

@ -78,7 +78,7 @@ class ParticipateController extends Controller
/** @var \App\Models\Customer */ /** @var \App\Models\Customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
/** @var \App\Models\Code */ /** @var \App\Models\Code|null */
$code = $request->post('unique_code'); $code = $request->post('unique_code');
/** @var \App\Models\Product|null */ /** @var \App\Models\Product|null */
$product = $request->post('product'); $product = $request->post('product');
@ -150,7 +150,7 @@ class ParticipateController extends Controller
$participation->created_at, $participation->created_at,
$instantWin->prize->label, $instantWin->prize->label,
$crmCustomer, $crmCustomer,
$code->code_unique, optional($code)->code_unique,
optional($product)->ean_code optional($product)->ean_code
)); ));

View file

@ -39,7 +39,9 @@ class StoreRangeRequest extends FormRequest
public function rules() public function rules()
{ {
$rules = [ $rules = [
'name' => 'required|string|min:2|max:255', 'name' => 'required|string|min:2|max:255',
'picture_alt' => 'nullable|required_with:picture|string|between:3,255',
'picture_title' => 'nullable|required_with:picture|string|between:3,255',
]; ];
$width = config('theme.models.ranges.picture_max_width'); $width = config('theme.models.ranges.picture_max_width');
$height = config('theme.models.ranges.picture_max_height'); $height = config('theme.models.ranges.picture_max_height');

View file

@ -2,10 +2,12 @@
namespace App\Http\Requests\Bo\Settings; namespace App\Http\Requests\Bo\Settings;
use App\Enums\CodeType;
use App\Lib\Helpers\ToolboxHelper; use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rules\Enum;
class UpdateSettingsRequest extends FormRequest class UpdateSettingsRequest extends FormRequest
{ {
@ -14,7 +16,7 @@ class UpdateSettingsRequest extends FormRequest
* *
* @return boolean * @return boolean
*/ */
public function authorize() public function authorize(): bool
{ {
return Gate::check('update', $this->route('settings')); return Gate::check('update', $this->route('settings'));
} }
@ -23,28 +25,26 @@ class UpdateSettingsRequest extends FormRequest
* Prepare the data for validation. * Prepare the data for validation.
* *
* @return void * @return void
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/ */
protected function prepareForValidation() protected function prepareForValidation(): void
{ {
// phpcs:enable // phpcs:enable
$this->merge([ $this->merge([
// Checkboxes. // Checkboxes.
'notify_prize_sent' => $this->boolean('notify_prize_sent'), 'notify_prize_sent' => $this->boolean('notify_prize_sent'),
'use_ean_codes' => $this->boolean('use_ean_codes'),
'activated_teaser' => $this->boolean('activated_teaser'), 'activated_teaser' => $this->boolean('activated_teaser'),
'activated_close_page' => $this->boolean('activated_close_page'), 'activated_close_page' => $this->boolean('activated_close_page'),
'discounts_activated' => $this->boolean('discounts_activated'), 'discounts_activated' => $this->boolean('discounts_activated'),
'winning_proof' => $this->boolean('winning_proof'), 'winning_proof' => $this->boolean('winning_proof'),
// Arrays. // Arrays.
'notification_emails' => collect($this->input('notification_emails'))->filter()->all(), 'notification_emails' => collect($this->input('notification_emails'))->filter()->all(),
// Remove some dates if the checkbox is uncheck. // Remove some dates if the checkbox is uncheck.
'close_page_active_date' => $this->input('activated_close_page') ? 'close_page_active_date' => $this->input('activated_close_page') ?
$this->input('close_page_active_date') : null, $this->input('close_page_active_date') : null,
// Remove discounts datas if the discount checkbox is uncheck. // Remove discounts datas if the discount checkbox is uncheck.
'discounts_site' => $this->input('discounts_activated') ? $this->input('discounts_site') : null, 'discounts_site' => $this->input('discounts_activated') ? $this->input('discounts_site') : null,
'discounts_campaign' => $this->input('discounts_activated') ? $this->input('discounts_campaign') : null, 'discounts_campaign' => $this->input('discounts_activated') ? $this->input('discounts_campaign') : null,
'web_coupons' => collect($this->input('web_coupons'))->filter()->all(), 'web_coupons' => collect($this->input('web_coupons'))->filter()->all(),
]); ]);
} }
@ -53,29 +53,30 @@ class UpdateSettingsRequest extends FormRequest
* *
* @return array * @return array
*/ */
public function rules() public function rules(): array
{ {
return [ return [
'name' => 'required|string|min:3|max:255', 'name' => 'required|string|min:3|max:255',
'company' => 'required|string|min:3|max:255', 'company' => 'required|string|min:3|max:255',
'start_date' => 'required|date', 'start_date' => 'required|date',
'end_date' => 'required|date', 'end_date' => 'required|date',
'nb_participation_player' => 'required|numeric|min:0', 'nb_participation_player' => 'required|numeric|min:0',
'nb_participation_ip' => 'required|numeric|min:0', 'nb_participation_ip' => 'required|numeric|min:0',
'nb_unique_code_use' => 'required|numeric|min:0', 'nb_unique_code_use' => 'required|numeric|min:0',
'use_ean_codes' => 'required|boolean', 'code_type' => ['required', new Enum(CodeType::class)],
'activated_teaser' => 'required|boolean', 'activated_teaser' => 'required|boolean',
'activated_close_page' => 'required|boolean', 'activated_close_page' => 'required|boolean',
// phpcs:ignore Generic.Files.LineLength.TooLong 'close_page_active_date' => [
'close_page_active_date' => 'nullable|required_if:activated_close_page,true,1|date|after_or_equal:end_date', 'nullable',
'required_if:activated_close_page,true,1',
'date',
'after_or_equal:end_date'
],
'redirect_close' => 'required|url:http,https', 'redirect_close' => 'required|url:http,https',
'notify_prize_sent' => 'required|boolean', 'notify_prize_sent' => 'required|boolean',
'discounts_activated' => 'required|boolean', 'discounts_activated' => 'required|boolean',
'discounts_site' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255', 'discounts_site' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255',
'discounts_campaign' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255', 'discounts_campaign' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255',
'web_coupons' => [ 'web_coupons' => [
'present', 'array', function ($attribute, $value, $fail) { 'present', 'array', function ($attribute, $value, $fail) {
if ($this->post('discounts_activated') and !collect($value)->filter()->count()) { if ($this->post('discounts_activated') and !collect($value)->filter()->count()) {
@ -103,7 +104,7 @@ class UpdateSettingsRequest extends FormRequest
$attribute, $attribute,
$value, $value,
$fail, $fail,
(object)[ (object) [
'maxHeight' => 409, 'maxHeight' => 409,
'maxWith' => 920, 'maxWith' => 920,
'minHeight' => 409, 'minHeight' => 409,
@ -112,10 +113,8 @@ class UpdateSettingsRequest extends FormRequest
); );
} }
], ],
'notification_emails' => 'required|array|min:1', 'notification_emails' => 'required|array|min:1',
'notification_emails.*' => 'email:rfc,spoof,dns', 'notification_emails.*' => 'email:rfc,spoof,dns',
'winning_proof' => 'required|boolean', 'winning_proof' => 'required|boolean',
]; ];
} }
@ -128,33 +127,31 @@ class UpdateSettingsRequest extends FormRequest
public function attributes(): array public function attributes(): array
{ {
return [ return [
'name' => trans('validation.attributes.name'), 'name' => trans('validation.attributes.name'),
'company' => trans('validation.custom.company'), 'company' => trans('validation.custom.company'),
'start_date' => trans('validation.custom.start_date'), 'start_date' => trans('validation.custom.start_date'),
'end_date' => trans('validation.custom.end_date'), 'end_date' => trans('validation.custom.end_date'),
'nb_participation_player' => trans('validation.custom.nb_participation_player'), 'nb_participation_player' => trans('validation.custom.nb_participation_player'),
'nb_participation_ip' => trans('validation.custom.nb_participation_ip'), 'nb_participation_ip' => trans('validation.custom.nb_participation_ip'),
'nb_unique_code_use' => trans('validation.custom.nb_unique_code_use'), 'nb_unique_code_use' => trans('validation.custom.nb_unique_code_use'),
'use_ean_codes' => trans('validation.custom.use_ean_codes'), 'code_type' => trans('validation.custom.code_type'),
'activated_teaser' => trans('validation.custom.activated_teaser'), 'activated_teaser' => trans('validation.custom.activated_teaser'),
'activated_close_page' => trans('validation.custom.activated_close_page'), 'activated_close_page' => trans('validation.custom.activated_close_page'),
'close_page_active_date' => trans('validation.custom.close_page_active_date'), 'close_page_active_date' => trans('validation.custom.close_page_active_date'),
'redirect_close' => trans('validation.custom.redirect_close'), 'redirect_close' => trans('validation.custom.redirect_close'),
'winning_proof' => trans('validation.custom.winning_proof'), 'winning_proof' => trans('validation.custom.winning_proof'),
'discounts_activated' => trans('validation.custom.discounts_activated'), 'discounts_activated' => trans('validation.custom.discounts_activated'),
'discounts_site' => trans('validation.custom.discounts_site'), 'discounts_site' => trans('validation.custom.discounts_site'),
'discounts_campaign' => trans('validation.custom.discounts_campaign'), 'discounts_campaign' => trans('validation.custom.discounts_campaign'),
'notify_prize_sent' => trans('validation.custom.notify_prize_sent'), 'notify_prize_sent' => trans('validation.custom.notify_prize_sent'),
'web_coupons' => trans('Tableau des coupons'),
'web_coupons' => trans('Tableau des coupons'),
'web_coupons.*.web_coupon_identifiant' => trans('Identifiant coupon :index'), 'web_coupons.*.web_coupon_identifiant' => trans('Identifiant coupon :index'),
'web_coupons.*.percentage' => trans('Pourcentage :index'), 'web_coupons.*.percentage' => trans('Pourcentage :index'),
'web_coupons.*.face_value' => trans('Valeur faciale :index'), 'web_coupons.*.face_value' => trans('Valeur faciale :index'),
'web_coupons.*.valid_on' => trans('Valable sur :index'), 'web_coupons.*.valid_on' => trans('Valable sur :index'),
'web_coupons.*.img_name' => trans('Nom image :index'), 'web_coupons.*.img_name' => trans('Nom image :index'),
'notification_emails' => trans('Tableau des email à notifier'),
'notification_emails' => trans('Tableau des email à notifier'), 'notification_emails.*.email' => trans('validation.attributes.email')
'notification_emails.*.email' => trans('validation.attributes.email')
]; ];
} }
} }

View file

@ -26,7 +26,7 @@ class UpdateStaticPageRequest extends FormRequest
public function prepareForValidation(): void public function prepareForValidation(): void
{ {
$this->merge([ $this->merge([
'content' => (string)Str::of($this->input('content')) 'content' => (string) Str::of($this->input('content'))
]); ]);
} }
@ -38,10 +38,10 @@ class UpdateStaticPageRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'seo_title' => 'required|min:45|max:70', 'seo_title' => 'required|min:45|max:70',
'seo_description' => 'required|min:50|max:160', 'seo_description' => 'required|min:50|max:160',
'title' => 'required|min:3|max:255', 'title' => 'required|min:3|max:255',
'content' => 'present', 'content' => 'present',
]; ];
} }
@ -53,10 +53,10 @@ class UpdateStaticPageRequest extends FormRequest
public function attributes(): array public function attributes(): array
{ {
return [ return [
'seo_title' => trans('validation.custom.seo_title'), 'seo_title' => trans('validation.custom.seo_title'),
'seo_description' => trans('validation.custom.seo_description'), 'seo_description' => trans('validation.custom.seo_description'),
'name' => trans('validation.attributes.title'), 'name' => trans('validation.attributes.title'),
'content' => trans('validation.attributes.content'), 'content' => trans('validation.attributes.content'),
]; ];
} }
} }

View file

@ -46,7 +46,7 @@ class CreateAccountRequest extends FormRequest
*/ */
public function rules() public function rules()
{ {
/** @var \App\Models\Customer */ /** @var \App\Models\Customer|null */
$authUser = auth('frontend')->user(); $authUser = auth('frontend')->user();
$countryCode = null; $countryCode = null;
@ -96,7 +96,7 @@ class CreateAccountRequest extends FormRequest
'email:rfc,dns,spoof', 'email:rfc,dns,spoof',
'max:255', 'max:255',
// * Does email already exists in CRM ? // * Does email already exists in CRM ?
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function (string $attribute, $value, \Closure $fail) { function (string $attribute, $value, \Closure $fail) {
if (CrmCustomer::findWithEmail(\strval($value))) { if (CrmCustomer::findWithEmail(\strval($value))) {
$fail('Un compte existe déjà avec cette adresse.'); $fail('Un compte existe déjà avec cette adresse.');

View file

@ -2,11 +2,12 @@
namespace App\Http\Requests\Fo; namespace App\Http\Requests\Fo;
use App\Enums\CodeType;
use App\Models\Code; use App\Models\Code;
use App\Models\Participation; use App\Models\Participation;
use App\Models\Product; use App\Models\Product;
use App\Models\Settings; use App\Models\Settings;
use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
@ -40,29 +41,27 @@ class ParticipateRequest extends FormRequest
*/ */
protected function prepareForValidation(): void protected function prepareForValidation(): void
{ {
try {
$this->merge([
'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([
'unique_code' => false,
]);
} finally {
$this->merge([
'ean_code' => (string)$this->string('ean_code')->padLeft(13, '0'),
'game_rules' => $this->boolean('game_rules')
]);
}
/** @var \App\Models\Settings */ /** @var \App\Models\Settings */
$settings = app(Settings::class); $settings = app(Settings::class);
if ($settings->code_type->equals(CodeType::unique) || $settings->code_type->equals(CodeType::both)) {
// ? Does the game use EAN Codes ? try {
if ($settings->use_ean_codes) { $this->merge([
'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([
'unique_code' => false,
]);
} finally {
$this->merge([
'game_rules' => $this->boolean('game_rules'),
]);
}
}
if ($settings->code_type->equals(CodeType::ean) || $settings->code_type->equals(CodeType::both)) {
/** /**
* * Check si les codes ean sont utilisés, auquel cas * * Check si les codes ean sont utilisés, auquel cas
* * on test celui envoyé pour savoir s'il est correct. * * on test celui envoyé pour savoir s'il est correct.
@ -75,12 +74,14 @@ class ParticipateRequest extends FormRequest
* càd que les code ean 8 sont de longueur 8, 7 de longueur7 et 13 de longueur 13. * càd que les code ean 8 sont de longueur 8, 7 de longueur7 et 13 de longueur 13.
*/ */
$this->merge([ $this->merge([
'ean_code' => (string) $this->string('ean_code')->padLeft(13, '0'),
// * Union pour faire des recherches séparée en une seule requête. // * Union pour faire des recherches séparée en une seule requête.
// ! ean_code is already str_padded to 13 // ! ean_code is already str_padded to 13
'product' => Product::query()->where('ean_code', $this->post('ean_code')) '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'), 8, '')))
->union(Product::query()->where('ean_code', Str::limit($this->post('ean_code'), 7, ''))) ->union(Product::query()->where('ean_code', Str::limit($this->post('ean_code'), 7, '')))
->first() ->first(),
'game_rules' => $this->boolean('game_rules'),
]); ]);
} //end if } //end if
} }
@ -96,10 +97,14 @@ class ParticipateRequest extends FormRequest
$settings = app(Settings::class); $settings = app(Settings::class);
$rules = [ $rules = [
'game_rules' => 'required|boolean|accepted', 'game_rules' => 'required|boolean|accepted',
'unique_code' => [ ];
// ? Does the game use unique Codes ?
if ($settings->code_type->equals(CodeType::unique) || $settings->code_type->equals(CodeType::both)) {
$rules = \array_merge($rules, ['unique_code' => [
'required', 'required',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function (string $attribute, $value, callable $fail) { function (string $attribute, $value, callable $fail) {
// ? Le Code unique est resolved ? // ? Le Code unique est resolved ?
if ($value === false or !\is_object($value) or \get_class($value) !== Code::class) { if ($value === false or !\is_object($value) or \get_class($value) !== Code::class) {
@ -129,15 +134,14 @@ class ParticipateRequest extends FormRequest
return; return;
} }
} }
] ]]);
]; } //end if
// ? Does the game use EAN Codes ? // ? Does the game use EAN Codes ?
if ($settings->use_ean_codes) { if ($settings->code_type->equals(CodeType::ean) || $settings->code_type->equals(CodeType::both)) {
$rules = \array_merge($rules, ['ean_code' => [ $rules = \array_merge($rules, ['ean_code' => [
'required', 'required',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) { function ($attribute, $value, $fail) {
// phpcs:enable // phpcs:enable
/** @var \App\Models\Produt|mixed */ /** @var \App\Models\Produt|mixed */
@ -162,9 +166,9 @@ class ParticipateRequest extends FormRequest
public function attributes(): array public function attributes(): array
{ {
return [ return [
'unique_code' => \trans('validation.custom.unique_code'), 'unique_code' => \trans('validation.custom.unique_code'),
'ean_code' => \trans('validation.custom.ean_code'), 'ean_code' => \trans('validation.custom.ean_code'),
'game_rules' => \trans('validation.custom.game_rules'), 'game_rules' => \trans('validation.custom.game_rules'),
]; ];
} }
} }

View file

@ -4,7 +4,7 @@ namespace App\Lib\Helpers;
use App\Models\Settings; use App\Models\Settings;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -348,11 +348,11 @@ class ToolboxHelper
/** /**
* Dynamic model query * Dynamic model query
* *
* @param string $defaultLocale * @param string $defaultLocale
* @param \Illuminate\Contracts\Database\Eloquent\Builder $q * @param \Illuminate\Database\Eloquent\Builder $q
* @param string $column * @param string $column
* @param string $value * @param string $value
* @return \Illuminate\Contracts\Database\Eloquent\Builder * @return \Illuminate\Database\Eloquent\Builder
*/ */
public static function queryColumnWithLocales( public static function queryColumnWithLocales(
string $defaultLocale, string $defaultLocale,
@ -418,7 +418,7 @@ class ToolboxHelper
if (!(is_object($input) or is_array($input))) { if (!(is_object($input) or is_array($input))) {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input)); throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input));
} }
$ret = (array)$input; $ret = (array) $input;
foreach ($ret as &$item) { foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar. // Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ? $item = (is_object($item) or is_array($item)) ?
@ -440,7 +440,7 @@ class ToolboxHelper
if (!(is_object($input) or is_array($input))) { if (!(is_object($input) or is_array($input))) {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input)); throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input));
} }
$ret = (object)$input; $ret = (object) $input;
foreach ($ret as &$item) { foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar. // Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ? $item = (is_object($item) or is_array($item)) ?

View file

@ -24,7 +24,7 @@ class PrizeWonAdminMail extends Mailable implements ShouldQueue
* @param \Carbon\Carbon $participation_date The customer participation date. * @param \Carbon\Carbon $participation_date The customer participation date.
* @param string $prize_label The won prize label. * @param string $prize_label The won prize label.
* @param \LDC\LDCCRM\Models\Customer $customer The customer who won. * @param \LDC\LDCCRM\Models\Customer $customer The customer who won.
* @param string $unique_code The unique code used to participate. * @param string|null $unique_code The unique code used to participate if any.
* @param string|null $ean_code The product code used to participate if any. * @param string|null $ean_code The product code used to participate if any.
* *
* @return void * @return void
@ -34,8 +34,8 @@ class PrizeWonAdminMail extends Mailable implements ShouldQueue
protected Carbon $participation_date, protected Carbon $participation_date,
protected string $prize_label, protected string $prize_label,
protected Customer $customer, protected Customer $customer,
protected string $unique_code, protected ?string $unique_code,
protected string|null $ean_code protected ?string $ean_code
) { ) {
} }
@ -60,12 +60,14 @@ class PrizeWonAdminMail extends Mailable implements ShouldQueue
*/ */
public function content(): \Illuminate\Mail\Mailables\Content public function content(): \Illuminate\Mail\Mailables\Content
{ {
$participation = [ $participation = [];
trans('Participation :'), if (!is_null($this->unique_code)) {
"Code Unique : {$this->unique_code}", $participation = [
]; trans('Code Unique : :code', ['code' => $this->unique_code]),
if ($this->ean_code) { ];
$participation[] = "Code EAN : {$this->ean_code}"; }
if (!is_null($this->ean_code)) {
$participation[] = trans('Code EAN : :code', ['code' => $this->ean_code]);
} }
return new Content( return new Content(
htmlString: (new MailMessage()) htmlString: (new MailMessage())

View file

@ -26,7 +26,7 @@ class WinningProofAdminMail extends Mailable implements ShouldQueue
* @param \Carbon\Carbon $participation_date The customer participation date. * @param \Carbon\Carbon $participation_date The customer participation date.
* @param string $prize_label The won prize label. * @param string $prize_label The won prize label.
* @param \LDC\LDCCRM\Models\Customer $customer The customer who won. * @param \LDC\LDCCRM\Models\Customer $customer The customer who won.
* @param string $unique_code The unique code used to participate. * @param string|null $unique_code The unique code used to participate if any.
* @param string|null $ean_code The product code used to participate if any. * @param string|null $ean_code The product code used to participate if any.
* @param string $sticker Fichier image du sticker utilisé. * @param string $sticker Fichier image du sticker utilisé.
* @param string $ticket Fichier image du ticket de caisse prouvant * @param string $ticket Fichier image du ticket de caisse prouvant
@ -40,8 +40,8 @@ class WinningProofAdminMail extends Mailable implements ShouldQueue
protected Carbon $participation_date, protected Carbon $participation_date,
protected string $prize_label, protected string $prize_label,
protected Customer $customer, protected Customer $customer,
protected string $unique_code, protected ?string $unique_code,
protected string|null $ean_code, protected ?string $ean_code,
protected string $sticker, protected string $sticker,
protected string $ticket, protected string $ticket,
protected string $barcode, protected string $barcode,
@ -70,11 +70,13 @@ class WinningProofAdminMail extends Mailable implements ShouldQueue
*/ */
public function content(): \Illuminate\Mail\Mailables\Content public function content(): \Illuminate\Mail\Mailables\Content
{ {
$participation = [ $participation = [];
trans('Participation :'), if (!is_null($this->ean_code)) {
trans('Code Unique : :code', ['code' => $this->unique_code]), $participation = [
]; trans('Code Unique : :code', ['code' => $this->unique_code]),
if ($this->ean_code) { ];
}
if (!is_null($this->ean_code)) {
$participation[] = trans('Code EAN : :code', ['code' => $this->ean_code]); $participation[] = trans('Code EAN : :code', ['code' => $this->ean_code]);
} }
return new Content( return new Content(

View file

@ -31,7 +31,7 @@ class BannedIp extends Model
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array<string> * @var array<int, string>
*/ */
protected $fillable = ['ip']; protected $fillable = ['ip'];

View file

@ -41,7 +41,7 @@ class Code extends Model
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array<string> * @var array<int, string>
*/ */
protected $fillable = [ protected $fillable = [
'code_exported', 'code_exported',

View file

@ -8,7 +8,7 @@ use App\Notifications\Customer\ResetPassword;
use App\Notifications\Customer\VerifyEmail; use App\Notifications\Customer\VerifyEmail;
use App\Traits\Models\ActivityLog; use App\Traits\Models\ActivityLog;
use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Contracts\Database\Query\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Prunable; use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
@ -365,19 +365,19 @@ class Customer extends Authenticatable implements MustVerifyEmail
if ($this->hashcode) { if ($this->hashcode) {
return collect(array_merge( return collect(array_merge(
parent::toArray(), parent::toArray(),
(array)$this->api_data (array) $this->api_data
))->intersectByKeys([ ))->intersectByKeys([
'email' => '', 'email' => '',
'firstname' => '', 'firstname' => '',
'lastname' => '', 'lastname' => '',
'address' => '', 'address' => '',
'address2' => '', 'address2' => '',
'postcode' => '', 'postcode' => '',
'birth_date' => '', 'birth_date' => '',
'city' => '', 'city' => '',
'country' => '', 'country' => '',
'opt_in' => '', 'opt_in' => '',
'phone' => '' 'phone' => ''
])->toArray(); ])->toArray();
} else { } else {
return parent::toArray(); return parent::toArray();

View file

@ -29,7 +29,7 @@ class FaqQuestion extends Model
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array<string> * @var array<int, string>
*/ */
protected $fillable = [ protected $fillable = [
'question', 'question',
@ -40,7 +40,7 @@ class FaqQuestion extends Model
/** /**
* The relations to eager load on every query. * The relations to eager load on every query.
* *
* @var array * @var array<int, string>
*/ */
protected $with = ['theme']; protected $with = ['theme'];

View file

@ -31,7 +31,7 @@ class FaqTheme extends Model
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array<string> * @var array<int, string>
*/ */
protected $fillable = ['name']; protected $fillable = ['name'];

View file

@ -32,7 +32,7 @@ class InstantWin extends Model
/** /**
* The relations to eager load on every query. * The relations to eager load on every query.
* *
* @var array * @var array<int, string>
*/ */
protected $with = ['prize']; protected $with = ['prize'];

View file

@ -6,7 +6,7 @@ use App\Casts\Ip;
use App\Lib\Helpers\FileStorageHelper; use App\Lib\Helpers\FileStorageHelper;
use App\Notifications\Customer\PrizeHasBeenSent; use App\Notifications\Customer\PrizeHasBeenSent;
use App\Traits\Models\ActivityLog; use App\Traits\Models\ActivityLog;
use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -42,7 +42,7 @@ class Participation extends Model
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array<string> * @var array<int,string>
*/ */
protected $fillable = [ protected $fillable = [
'barcode', 'barcode',
@ -56,18 +56,18 @@ class Participation extends Model
* @var array<string, string> * @var array<string, string>
*/ */
protected $casts = [ protected $casts = [
'ip' => Ip::class, 'ip' => Ip::class,
'price_sent' => 'bool' 'price_sent' => 'bool'
]; ];
/** /**
* The model's attributes. * The model's attributes.
* *
* @var array * @var array<string,mixed>
*/ */
protected $attributes = [ protected $attributes = [
'sticker' => null, 'sticker' => null,
'ticket' => null, 'ticket' => null,
'barcode' => null, 'barcode' => null,
]; ];
@ -76,7 +76,7 @@ class Participation extends Model
* *
* @return void * @return void
*/ */
protected static function booted() protected static function booted(): void
{ {
static::creating(function (self $model) { static::creating(function (self $model) {
self::manageFields($model); self::manageFields($model);
@ -192,21 +192,23 @@ class Participation extends Model
* *
* @param \IPLib\Address\AddressInterface $ip Ip for the participation. * @param \IPLib\Address\AddressInterface $ip Ip for the participation.
* @param \App\Models\Customer $customer The Customer playing the game. * @param \App\Models\Customer $customer The Customer playing the game.
* @param \App\Models\Code $code The unique code used to play. * @param \App\Models\Code|null $code The unique code used to play.
* @param null|\App\Models\Product $product The product that holds the sticker if codes are actived. * @param \App\Models\Product|null $product The product that holds the sticker if codes are actived.
* @return \App\Models\Participation * @return \App\Models\Participation
*/ */
public static function makeNew( public static function makeNew(
AddressInterface $ip, AddressInterface $ip,
Customer $customer, Customer $customer,
Code $code, ?Code $code = null,
Product|null $product = null ?Product $product = null
): self { ): self {
$self = new self(); $self = new self();
$self->ip = $ip; $self->ip = $ip;
$self->customer()->associate($customer); $self->customer()->associate($customer);
$self->code()->associate($code); if (!is_null($code)) {
if ($product) { $self->code()->associate($code);
}
if (!is_null($product)) {
$self->product()->associate($product); $self->product()->associate($product);
} }
return $self; return $self;

View file

@ -39,7 +39,7 @@ class Prize extends Model
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array<string> * @var array<int, string>
*/ */
protected $fillable = [ protected $fillable = [
'label', 'label',

View file

@ -36,7 +36,7 @@ class Product extends Model
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array<string> * @var array<int, string>
*/ */
protected $fillable = [ protected $fillable = [
'name', 'name',

View file

@ -11,10 +11,12 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
/** /**
* Range. * Range.
* *
* @property string $name Name * @property string $name Name
* @property string $picture Path of the picture * @property string $picture Path of the picture
* @property-read \Illuminate\Support\Carbon $created_at Creation Date. * @property string $picture_alt Alt text for the picture
* @property-read \Illuminate\Support\Carbon $updated_at Last update Date. * @property string $picture_title Title text for the picture
* @property-read \Illuminate\Support\Carbon $created_at Creation Date.
* @property-read \Illuminate\Support\Carbon $updated_at Last update Date.
* *
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Product[] $products * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Product[] $products
* Associated products. * Associated products.
@ -29,11 +31,13 @@ class Range extends Model
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array<string> * @var array<int, string>
*/ */
protected $fillable = [ protected $fillable = [
'name', 'name',
'picture' 'picture',
'picture_alt',
'picture_title',
]; ];
// * EVENTS // * EVENTS

View file

@ -3,6 +3,7 @@
namespace App\Models; namespace App\Models;
use App\Casts\AsArrayOfDiscount; use App\Casts\AsArrayOfDiscount;
use App\Enums\CodeType;
use App\Lib\Discount; use App\Lib\Discount;
use App\Lib\Helpers\FileStorageHelper; use App\Lib\Helpers\FileStorageHelper;
use App\Traits\Models\IsCachedAsCollection; use App\Traits\Models\IsCachedAsCollection;
@ -26,7 +27,7 @@ use Illuminate\Support\Facades\Cache;
* qu'une seule fois 1 code donné * qu'une seule fois 1 code donné
* @property boolean $notify_prize_sent Should the game send notifications * @property boolean $notify_prize_sent Should the game send notifications
* when the prize is sent ? * when the prize is sent ?
* @property boolean $use_ean_codes Should the game use ean codes ? * @property \App\Enums\CodeType $code_type Wich codes must be used.
* @property boolean $activated_teaser Is the teaser active ? * @property boolean $activated_teaser Is the teaser active ?
* @property boolean $activated_close_page Is the close page active ? * @property boolean $activated_close_page Is the close page active ?
* @property \Illuminate\Support\Carbon $close_page_active_date End date of the close page * @property \Illuminate\Support\Carbon $close_page_active_date End date of the close page
@ -56,7 +57,7 @@ class Settings extends Model
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array<string> * @var array<int, string>
*/ */
protected $fillable = [ protected $fillable = [
'name', 'name',
@ -66,7 +67,7 @@ class Settings extends Model
'nb_participation_player', 'nb_participation_player',
'nb_participation_ip', 'nb_participation_ip',
'nb_unique_code_use', 'nb_unique_code_use',
'use_ean_codes', 'code_type',
'activated_teaser', 'activated_teaser',
'activated_close_page', 'activated_close_page',
'close_page_active_date', 'close_page_active_date',
@ -87,17 +88,17 @@ class Settings extends Model
* @var array<string, string> * @var array<string, string>
*/ */
protected $casts = [ protected $casts = [
'notify_prize_sent' => 'bool', 'notify_prize_sent' => 'bool',
'use_ean_codes' => 'bool', 'code_type' => CodeType::class,
'activated_teaser' => 'bool', 'activated_teaser' => 'bool',
'activated_close_page' => 'bool', 'activated_close_page' => 'bool',
'discounts_activated' => 'bool', 'discounts_activated' => 'bool',
'winning_proof' => 'bool', 'winning_proof' => 'bool',
'start_date' => 'datetime', 'start_date' => 'datetime',
'end_date' => 'datetime', 'end_date' => 'datetime',
'close_page_active_date' => 'datetime', 'close_page_active_date' => 'datetime',
'web_coupons' => AsArrayOfDiscount::class, 'web_coupons' => AsArrayOfDiscount::class,
'notification_emails' => 'array', 'notification_emails' => 'array',
]; ];
// * EVENTS // * EVENTS
@ -144,14 +145,13 @@ class Settings extends Model
/** /**
* Check if the game can be play and report if it cannot. * Check if the game can be play and report if it cannot.
* Methods to check via policy if game can be played
* *
* Demande Vincent: Methods to check via policy if game can be played * Needed:
* * - Min 1 code unique if Settings code_type is unique or both codes.
* Besoin: * - Min 1 Instant gagnant.
* - Min 1 code unique * - Min 1 Product if Settings code_type is ean or both codes.
* - Min 1 Instant gagnant * - Min 1 WebCoupon if Settings discounts_activated
* - Min 1 Product si Settings use_ean_codes
* - Min 1 WebCoupon si Settings discounts_activated
* *
* @return boolean * @return boolean
*/ */
@ -159,23 +159,29 @@ class Settings extends Model
{ {
/** @var \App\Models\Settings */ /** @var \App\Models\Settings */
$settings = app(Settings::class); $settings = app(Settings::class);
if (!(bool)Prize::getCached()->count()) { if (!(bool) Prize::getCached()->count()) {
report(new \RuntimeException('Need prizes to enable the game')); report(new \RuntimeException('Need prizes to enable the game'));
return false; return false;
} }
if (!(bool)Cache::remember('codes.count', CarbonInterval::day(), fn () => Code::query()->count())) { if (
($settings->code_type->equals(CodeType::unique) || $settings->code_type->equals(CodeType::both))
and !(bool) Cache::remember('codes.count', CarbonInterval::day(), fn() => Code::query()->count())
) {
report(new \RuntimeException('Need codes to enable the game')); report(new \RuntimeException('Need codes to enable the game'));
return false; return false;
} }
if (!(bool)InstantWin::getCached()->count()) { if (!(bool) InstantWin::getCached()->count()) {
report(new \RuntimeException('Need instant win to enable the game')); report(new \RuntimeException('Need instant win to enable the game'));
return false; return false;
} }
if ($settings->use_ean_codes and !(bool)Product::getCached()->count()) { if (
($settings->code_type->equals(CodeType::ean) || $settings->code_type->equals(CodeType::both))
and !(bool) Product::getCached()->count()
) {
report(new \RuntimeException('Need products to enable the game using ean codes')); report(new \RuntimeException('Need products to enable the game using ean codes'));
return false; return false;
} }
if ($settings->discounts_activated and !(bool)collect($this->web_coupons)->count()) { if ($settings->discounts_activated and !(bool) collect($this->web_coupons)->count()) {
report(new \RuntimeException('Need MesBr coupons to enable the game using discounts')); report(new \RuntimeException('Need MesBr coupons to enable the game using discounts'));
return false; return false;
} }

View file

@ -9,7 +9,7 @@ use App\Models\Participation;
use App\Models\Settings; use App\Models\Settings;
use App\Models\User; use App\Models\User;
use App\Policies\Rules\UserStaticRules; use App\Policies\Rules\UserStaticRules;
use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
class ParticipationPolicy class ParticipationPolicy
{ {
@ -57,7 +57,7 @@ class ParticipationPolicy
false; false;
// * Deny banned IP, Check did not participate too much // * Deny banned IP, Check did not participate too much
return !(bool)BannedIp::getCached() return !(bool) BannedIp::getCached()
->where('ip', app('clientIp'))->count() and ->where('ip', app('clientIp'))->count() and
$playerCanParticipate; $playerCanParticipate;
} }
@ -74,7 +74,7 @@ class ParticipationPolicy
// * Recettage // * Recettage
return true; return true;
} }
return (bool)$authUser->participations()->where(function (Builder $q) { return (bool) $authUser->participations()->where(function (Builder $q) {
$q->whereHas('instantWin')->orWhereHas('discount'); $q->whereHas('instantWin')->orWhereHas('discount');
})->count(); })->count();
} }

View file

@ -37,28 +37,28 @@ class AuthServiceProvider extends ServiceProvider
// * POLICIES // * POLICIES
Gate::define( Gate::define(
'fo.showRe7', 'fo.showRe7',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
fn (User|Customer $user) => ($user instanceof User) and !app()->environment(['production']) and fn (User|Customer $user) => ($user instanceof User) and !app()->environment(['production']) and
$user->role->value() <= RoleEnum::marketing->value() $user->role->value() <= RoleEnum::marketing->value()
); );
Gate::define( Gate::define(
'fo.showProducts', 'fo.showProducts',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
fn (User|Customer|null $user) => View::exists('theme::pages.products') and fn (User|Customer|null $user) => View::exists('theme::pages.products') and
(bool)Product::getCached()->count() (bool) Product::getCached()->count()
); );
Gate::define( Gate::define(
'fo.showFaq', 'fo.showFaq',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
fn (User|Customer|null $user) => View::exists('theme::pages.faq') and fn (User|Customer|null $user) => View::exists('theme::pages.faq') and
(bool)FaqQuestion::getCached()->count() (bool) FaqQuestion::getCached()->count()
); );
Gate::define( Gate::define(
'fo.gameEnabled', 'fo.gameEnabled',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
function (User|Customer|null $user) { function (User|Customer|null $user) {
/** @var \App\Models\Settings */ /** @var \App\Models\Settings */
$settings = app(Settings::class); $settings = app(Settings::class);

View file

@ -109,7 +109,7 @@ trait BaseEnum
} }
$baseArray[$index] = $elements[$enum->name]; $baseArray[$index] = $elements[$enum->name];
} }
return [$enum->name => (object)$baseArray]; return [$enum->name => (object) $baseArray];
})->all()); })->all());
} }
return self::getCache('enums'); return self::getCache('enums');

View file

@ -56,7 +56,7 @@ trait HasPicture
?int $maxHeight = null, ?int $maxHeight = null,
bool $nullable = false bool $nullable = false
): array { ): array {
$sizes = (object)[ $sizes = (object) [
'minWith' => $minWith ?? $this->minWith, 'minWith' => $minWith ?? $this->minWith,
'minHeight' => $minHeight ?? $this->minHeight, 'minHeight' => $minHeight ?? $this->minHeight,
'maxWith' => $maxWith ?? $this->maxWith, 'maxWith' => $maxWith ?? $this->maxWith,
@ -89,7 +89,7 @@ trait HasPicture
?int $maxWith = null, ?int $maxWith = null,
?int $maxHeight = null ?int $maxHeight = null
): array { ): array {
$sizes = (object)[ $sizes = (object) [
'minWith' => $minWith ?? $this->minWith, 'minWith' => $minWith ?? $this->minWith,
'minHeight' => $minHeight ?? $this->minHeight, 'minHeight' => $minHeight ?? $this->minHeight,
'maxWith' => $maxWith ?? $this->maxWith, 'maxWith' => $maxWith ?? $this->maxWith,

View file

@ -1,6 +1,6 @@
{ {
"name": "uwil/gen-game-web", "name": "uwil/gen-game-web",
"version": "2.5.11", "version": "3.0.0",
"type": "project", "type": "project",
"description": "Game generator", "description": "Game generator",
"support": { "support": {

View file

@ -41,7 +41,7 @@ return [
| |
*/ */
'debug' => (bool)env('APP_DEBUG', false), 'debug' => (bool) env('APP_DEBUG', false),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View file

@ -55,7 +55,7 @@ return [
'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false), 'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false),
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces-sample-rate. // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces-sample-rate.
'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float)env('SENTRY_TRACES_SAMPLE_RATE'), 'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float) env('SENTRY_TRACES_SAMPLE_RATE'),
'before_send' => [\App\Lib\Helpers\ExceptionsHelper::class, 'sentryEventHandler'], 'before_send' => [\App\Lib\Helpers\ExceptionsHelper::class, 'sentryEventHandler'],
]; ];

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('use_ean_codes');
$table->unsignedTinyInteger('code_type')->comment('Type of codes used (CodeType Enum)')
->after('nb_unique_code_use');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('code_type');
$table->boolean('use_ean_codes')->comment('Le joueur doivent entrer le code EAN du produit porteur')
->after('nb_unique_code_use');
});
}
};

View file

@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('participations', function (Blueprint $table) {
$table->dropForeign('participations_code_unique_id_foreign');
$table->dropColumn('code_unique_id');
});
Schema::table('participations', function (Blueprint $table) {
$table->foreignId('code_unique_id')->nullable()->comment('Code unique utilisé')
->after('customer_id')->constrained('codes');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::table('participations', function (Blueprint $table) {
$table->dropForeign('participations_code_unique_id_foreign');
$table->dropColumn('code_unique_id');
});
Schema::table('participations', function (Blueprint $table) {
$table->foreignId('code_unique_id')->nullable(false)->comment('Code unique utilisé')
->after('customer_id')->constrained('codes');
});
}
};

View file

@ -2,6 +2,7 @@
namespace Database\Seeders\Mandatory; namespace Database\Seeders\Mandatory;
use App\Enums\CodeType;
use App\Models\Settings; use App\Models\Settings;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -13,7 +14,7 @@ class SettingsSeeder extends Seeder
* *
* @return void * @return void
*/ */
public function run() public function run(): void
{ {
DB::table('settings')->truncate(); DB::table('settings')->truncate();
$settings = new Settings(); $settings = new Settings();
@ -24,7 +25,7 @@ class SettingsSeeder extends Seeder
$settings->nb_participation_player = 30; $settings->nb_participation_player = 30;
$settings->nb_participation_ip = 30; $settings->nb_participation_ip = 30;
$settings->nb_unique_code_use = 1; $settings->nb_unique_code_use = 1;
$settings->use_ean_codes = false; $settings->code_type = CodeType::unique;
$settings->activated_teaser = false; $settings->activated_teaser = false;
$settings->activated_close_page = false; $settings->activated_close_page = false;
$settings->notify_prize_sent = false; $settings->notify_prize_sent = false;

9
package-lock.json generated
View file

@ -2686,9 +2686,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001615", "version": "1.0.30001727",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001615.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
"integrity": "sha512-1IpazM5G3r38meiae0bHRnPhz+CBQ3ZLqbQMtrg+AsTPKAXgW38JNsXkyZ+v8waCsDmPq87lmfun5Q2AGysNEQ==", "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -2703,7 +2703,8 @@
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
] ],
"license": "CC-BY-4.0"
}, },
"node_modules/chalk": { "node_modules/chalk": {
"version": "2.4.2", "version": "2.4.2",

View file

@ -29,8 +29,7 @@
<rule ref="Generic.Files.LineLength"/> <rule ref="Generic.Files.LineLength"/>
<rule ref="Generic.Formatting.DisallowMultipleStatements"/> <rule ref="Generic.Formatting.DisallowMultipleStatements"/>
<rule ref="Generic.Formatting.MultipleStatementAlignment"/> <rule ref="Generic.Formatting.MultipleStatementAlignment"/>
<rule ref="Generic.Formatting.NoSpaceAfterCast"/> <rule ref="Generic.Formatting.SpaceAfterCast"/>
<rule ref="Generic.Functions.CallTimePassByReference"/>
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/> <rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
<rule ref="Generic.Metrics.CyclomaticComplexity"/> <rule ref="Generic.Metrics.CyclomaticComplexity"/>
<rule ref="Generic.Metrics.NestingLevel"/> <rule ref="Generic.Metrics.NestingLevel"/>
@ -43,10 +42,8 @@
<rule ref="Generic.PHP.NoSilencedErrors"/> <rule ref="Generic.PHP.NoSilencedErrors"/>
<rule ref="Generic.Strings.UnnecessaryStringConcat"/> <rule ref="Generic.Strings.UnnecessaryStringConcat"/>
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/> <rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
<rule ref="Generic.WhiteSpace.LanguageConstructSpacing"/>
<rule ref="Generic.WhiteSpace.ScopeIndent"/> <rule ref="Generic.WhiteSpace.ScopeIndent"/>
<rule ref="MySource.PHP.EvalObjectFactory"/>
<rule ref="MySource.PHP.GetRequestData"/>
<rule ref="MySource.PHP.AjaxNullComparison"/>
<rule ref="PEAR.Classes.ClassDeclaration"/> <rule ref="PEAR.Classes.ClassDeclaration"/>
<rule ref="PEAR.Commenting.InlineComment"/> <rule ref="PEAR.Commenting.InlineComment"/>
<rule ref="PSR1.Classes.ClassDeclaration"/> <rule ref="PSR1.Classes.ClassDeclaration"/>
@ -79,7 +76,6 @@
<rule ref="Squiz.Scope.MemberVarScope"/> <rule ref="Squiz.Scope.MemberVarScope"/>
<rule ref="Squiz.Scope.MethodScope"/> <rule ref="Squiz.Scope.MethodScope"/>
<rule ref="Squiz.Scope.StaticThisUsage"/> <rule ref="Squiz.Scope.StaticThisUsage"/>
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing"/>
<rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/> <rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/>
<rule ref="Squiz.WhiteSpace.MemberVarSpacing"> <rule ref="Squiz.WhiteSpace.MemberVarSpacing">
<properties> <properties>

View file

@ -219,7 +219,6 @@ return [
:countries comme par exemple :examples', :countries comme par exemple :examples',
'phone' => 'Le champ :attribute n\'est pas un numéro de téléphone valide, il accepte les 'phone' => 'Le champ :attribute n\'est pas un numéro de téléphone valide, il accepte les
numéros des pays suivants :countries.', numéros des pays suivants :countries.',
'custom' => [ 'custom' => [
'of_the' => [ 'of_the' => [
'male' => 'du ', 'male' => 'du ',
@ -237,7 +236,6 @@ return [
'static_page_type' => 'type de page', 'static_page_type' => 'type de page',
'seo_title' => 'titre SEO', 'seo_title' => 'titre SEO',
'seo_description' => 'description SEO', 'seo_description' => 'description SEO',
'ip' => 'IP', 'ip' => 'IP',
'code' => 'code', 'code' => 'code',
'unique_code' => 'code unique', 'unique_code' => 'code unique',
@ -260,7 +258,7 @@ return [
'nb_participation_ip' => 'nombre de participation par adresse IP', 'nb_participation_ip' => 'nombre de participation par adresse IP',
'nb_unique_code_use' => 'nombre d\'utilisation du code unique', 'nb_unique_code_use' => 'nombre d\'utilisation du code unique',
'code_length' => 'longueur des code uniques', 'code_length' => 'longueur des code uniques',
'use_ean_codes' => 'activer les codes EAN', 'code_type' => 'type de code',
'activated_teaser' => 'activation du teaser', 'activated_teaser' => 'activation du teaser',
'activated_close_page' => 'activation page de fermeture', 'activated_close_page' => 'activation page de fermeture',
'close_page_active_date' => 'page active jusqu\'au', 'close_page_active_date' => 'page active jusqu\'au',
@ -274,7 +272,6 @@ return [
'game_rules' => 'règlement du jeu', 'game_rules' => 'règlement du jeu',
'notify_prize_sent' => 'notification de lot envoyé au participant', 'notify_prize_sent' => 'notification de lot envoyé au participant',
'comment' => 'commentaire', 'comment' => 'commentaire',
'barcode' => 'code-barres du produit', 'barcode' => 'code-barres du produit',
'ticket' => 'ticket de caisse avec la date', 'ticket' => 'ticket de caisse avec la date',
'sticker' => 'sticker du produit', 'sticker' => 'sticker du produit',

View file

@ -14,6 +14,7 @@ document.addEventListener("DOMContentLoaded", function () {
const inputDiscountsActivated = document.querySelector("input[name=discounts_activated]") as HTMLInputElement | null; const inputDiscountsActivated = document.querySelector("input[name=discounts_activated]") as HTMLInputElement | null;
const inputActivatedClosePage = document.querySelector("input[name=activated_close_page]") as HTMLInputElement | null; const inputActivatedClosePage = document.querySelector("input[name=activated_close_page]") as HTMLInputElement | null;
const selectCodeType = document.querySelector("select[name=code_type]") as HTMLSelectElement | null;
const discountsSite = document.getElementById("discounts-site") as HTMLElement | null; const discountsSite = document.getElementById("discounts-site") as HTMLElement | null;
const discountsCampaign = document.getElementById("discounts-campaign") as HTMLElement | null; const discountsCampaign = document.getElementById("discounts-campaign") as HTMLElement | null;
@ -24,6 +25,7 @@ document.addEventListener("DOMContentLoaded", function () {
const settingsClosePageActiveDate = document.getElementById("settings-close-page-active-date") as HTMLInputElement | null; const settingsClosePageActiveDate = document.getElementById("settings-close-page-active-date") as HTMLInputElement | null;
const settingsDiscountsSite = document.getElementById("settings-discounts-site") as HTMLInputElement | null; const settingsDiscountsSite = document.getElementById("settings-discounts-site") as HTMLInputElement | null;
const settingsDiscountsCampaign = document.getElementById("settings-discounts-campaign") as HTMLInputElement | null; const settingsDiscountsCampaign = document.getElementById("settings-discounts-campaign") as HTMLInputElement | null;
const settingsNbUniqueCodeUse = document.getElementById("settings-nb-unique-code-use") as HTMLElement | null;
if (!inputDiscountsActivated) { if (!inputDiscountsActivated) {
throw new Error("Missing input[name=discounts_activated]"); throw new Error("Missing input[name=discounts_activated]");
@ -31,6 +33,9 @@ document.addEventListener("DOMContentLoaded", function () {
if (!inputActivatedClosePage) { if (!inputActivatedClosePage) {
throw new Error("Missing input[name=activated_close_page]"); throw new Error("Missing input[name=activated_close_page]");
} }
if (!selectCodeType) {
throw new Error("Missing select[name=code_type]");
}
if (!discountsSite) { if (!discountsSite) {
throw new Error("Missing #discounts-site"); throw new Error("Missing #discounts-site");
} }
@ -52,6 +57,9 @@ document.addEventListener("DOMContentLoaded", function () {
if (!settingsDiscountsCampaign) { if (!settingsDiscountsCampaign) {
throw new Error("Missing #settings-discounts-campaign"); throw new Error("Missing #settings-discounts-campaign");
} }
if (!settingsNbUniqueCodeUse) {
throw new Error("Missing #settings-nb-unique-code-use");
}
const discountsWebCouponsInputs = discountsWebCoupons.getElementsByTagName("input"); const discountsWebCouponsInputs = discountsWebCoupons.getElementsByTagName("input");
@ -87,6 +95,11 @@ document.addEventListener("DOMContentLoaded", function () {
} }
}; };
const toggleNbUniqueCodeUse = (): void => {
const shouldHide = selectCodeType.value === settingsNbUniqueCodeUse.dataset.enumHide;
settingsNbUniqueCodeUse.classList.toggle("d-none", shouldHide);
};
// Gestion du tableau pour discount // Gestion du tableau pour discount
inputDiscountsActivated.addEventListener("change", toggleDiscount); inputDiscountsActivated.addEventListener("change", toggleDiscount);
toggleDiscount(); toggleDiscount();
@ -94,4 +107,8 @@ document.addEventListener("DOMContentLoaded", function () {
// Gestion de l'activation de la page de fermeture (champ date à afficher ou non) // Gestion de l'activation de la page de fermeture (champ date à afficher ou non)
inputActivatedClosePage.addEventListener("change", toggleClosePage); inputActivatedClosePage.addEventListener("change", toggleClosePage);
toggleClosePage(); toggleClosePage();
// Gestion du nombre d'utilisation du code unique
selectCodeType.addEventListener("change", toggleNbUniqueCodeUse);
toggleNbUniqueCodeUse();
}); });

View file

@ -13,7 +13,8 @@
<p> <p>
{{ __('Nombre de participations total') }} : <b>{{ $participationsTotal }}</b><br> {{ __('Nombre de participations total') }} : <b>{{ $participationsTotal }}</b><br>
{{ __('Nombre de participations moyenne par joueur') }} : <b>{{ $avgParticipUser }}</b><br> {{ __('Nombre de participations moyenne par joueur') }} : <b>{{ $avgParticipUser }}</b><br>
@if ($settings->use_ean_codes) @php $codeTypeEnum = \App\Enums\CodeType::class; @endphp
@if ($settings->code_type->equals($codeTypeEnum::ean) || $settings->code_type->equals($codeTypeEnum::both))
{{ __('Top 3 des codes EAN les plus utilisés') }} : {{ __('Top 3 des codes EAN les plus utilisés') }} :
<ul class="list-group list-group-horizontal"> <ul class="list-group list-group-horizontal">
@foreach ($codesEan as $codeEan) @foreach ($codesEan as $codeEan)

View file

@ -11,7 +11,9 @@
</li> </li>
<li class="col-md-6"> <li class="col-md-6">
<span>{{ __('Longueur des codes uniques') }}&nbsp;</span> <span>{{ __('Longueur des codes uniques') }}&nbsp;</span>
<span class="fw-bold fs-5 font-monospace d-inline-block m-0">{{ config('codes.codeLength') }}</span> <span class="fw-bold fs-5 font-monospace d-inline-block m-0">
{{ config('codes.codeLength') }}
</span>
</li> </li>
<li class="col-md-6"> <li class="col-md-6">
<span>{{ __('Longueur des codes uniques') }}&nbsp;</span> <span>{{ __('Longueur des codes uniques') }}&nbsp;</span>
@ -180,8 +182,29 @@
</div> </div>
</fieldset> </fieldset>
</div> </div>
{{-- NB_UNIQUE_CODE_USE --}} {{-- CODES TYPES --}}
<div class="col-12 col-md-6 mb-3"> <div class="col-12 col-md-6 mb-3">
<fieldset class="h-100 border p-3">
<legend>{{ str(__('validation.custom.code_type'))->ucfirst() }}</legend>
<div>
@include('back::partials.inputs.tinyint-enum', [
'id' => 'codeType',
'label' => str(__('validation.custom.code_type'))->ucfirst(),
'name' => 'code_type',
'model' => $settingsModel ?? null,
'required' => true,
'disabled' => false,
'value' => optional(optional($settingsModel ?? null)->code_type)->value(),
'associatedModels' => \App\Enums\CodeType::toArray(),
'helper' => __('Sélectionner un :field pour le jeu', [
'field' => __('validation.custom.code_type'),
]),
])
</div>
</fieldset>
</div>
{{-- NB_UNIQUE_CODE_USE --}}
<div id="settings-nb-unique-code-use" class="col-12 col-md-6 mb-3" data-enum-hide="{{ \App\Enums\CodeType::ean->value() }}">
<fieldset class="h-100 border p-3"> <fieldset class="h-100 border p-3">
<legend>{{ Str::ucfirst(__('validation.custom.nb_unique_code_use')) }}</legend> <legend>{{ Str::ucfirst(__('validation.custom.nb_unique_code_use')) }}</legend>
<div> <div>
@ -294,25 +317,6 @@
{{-- Force next columns to break to new line --}} {{-- Force next columns to break to new line --}}
<div class="w-100"></div> <div class="w-100"></div>
{{-- USE_EAN_CODES --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="h-100 border p-3">
<legend>{{ Str::ucfirst(__('validation.custom.use_ean_codes')) }}</legend>
<div>
@include('back::partials.inputs.tinyint-boolean', [
'id' => 'settings-use-ean-codes',
'label' => Str::ucfirst(__('validation.custom.use_ean_codes')),
'name' => 'use_ean_codes',
'required' => false,
'model' => $settingsModel ?? null,
'helper' => __('validation.boolean', [
'attribute' => Str::ucfirst(__('validation.custom.use_ean_codes')),
]),
])
</div>
</fieldset>
</div>
{{-- WINNING_PROOF --}} {{-- WINNING_PROOF --}}
<div class="col-12 col-md-6 mb-3"> <div class="col-12 col-md-6 mb-3">
<fieldset class="h-100 border p-3"> <fieldset class="h-100 border p-3">

View file

@ -7,10 +7,13 @@
'email' => __('Email'), 'email' => __('Email'),
'prize' => __('Lot'), 'prize' => __('Lot'),
]; ];
if ($settingsGlobal->use_ean_codes) { $codeTypeEnum = \App\Enums\CodeType::class;
if ($settingsGlobal->code_type->equals($codeTypeEnum::ean) || $settingsGlobal->code_type->equals($codeTypeEnum::both)) {
$cols['ean_code_id'] = __('Code EAN'); $cols['ean_code_id'] = __('Code EAN');
} }
$cols['code_unique_id'] = __('Code unique'); if ($settingsGlobal->code_type->equals($codeTypeEnum::unique) || $settingsGlobal->code_type->equals($codeTypeEnum::both)) {
$cols['code_unique_id'] = __('Code unique');
}
$cols['price_sent'] = __('Gain envoyé ?'); $cols['price_sent'] = __('Gain envoyé ?');
if ($settingsGlobal->winning_proof) { if ($settingsGlobal->winning_proof) {
$cols['proof'] = __('Preuves'); $cols['proof'] = __('Preuves');
@ -38,14 +41,16 @@
{{ __('N° :number', ['number' => $participationModel->instantWin->prize->id]) }} {{ __('N° :number', ['number' => $participationModel->instantWin->prize->id]) }}
</a> </a>
</td> </td>
@if ($settingsGlobal->use_ean_codes) @if ($settingsGlobal->code_type->equals($codeTypeEnum::ean) || $settingsGlobal->code_type->equals($codeTypeEnum::both))
<td class="text-center align-middle"> <td class="text-center align-middle">
<span>{{ $participationModel->codeEan }}</span> <span>{{ $participationModel->product->ean_code }}</span>
</td>
@endif
@if ($settingsGlobal->code_type->equals($codeTypeEnum::unique) || $settingsGlobal->code_type->equals($codeTypeEnum::both))
<td class="text-center align-middle">
<span>{{ $participationModel->code_unique_id }}</span>
</td> </td>
@endif @endif
<td class="text-center align-middle">
<span>{{ $participationModel->code->code_unique }}</span>
</td>
<td class="text-center align-middle"> <td class="text-center align-middle">
@can('changePrizeSent', $participationModel) @can('changePrizeSent', $participationModel)
<form class="confirm-mark-sent" action="{{ route('bo.participations.change-prize-sent', $participationModel) }}" <form class="confirm-mark-sent" action="{{ route('bo.participations.change-prize-sent', $participationModel) }}"