diff --git a/.env.example b/.env.example index 0937081a..40622e7f 100644 --- a/.env.example +++ b/.env.example @@ -73,3 +73,8 @@ SAML2_CONTACT_SUPPORT_EMAIL= GOOGLE_RECAPTCHA_PUBLIC= GOOGLE_RECAPTCHA_PRIVATE= GOOGLE_RECAPTCHA_VERSION= + +## MERCANET +MERCANET_SECRET= +MERCANET_MERCHAND_ID= +MERCANET_ENV= diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index a888e4bf..10562210 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -16,7 +16,7 @@ class Kernel extends ConsoleKernel * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */ - protected function schedule(Schedule $schedule) + protected function schedule(Schedule $schedule): void { $schedule->job(FetchDigitalRecruiterOffers::class)->daily(); $schedule->job(DeleteOldMessages::class)->dailyAt('14:15'); @@ -28,7 +28,7 @@ class Kernel extends ConsoleKernel * * @return void */ - protected function commands() + protected function commands(): void { $this->load(__DIR__ . '/Commands'); diff --git a/app/Enums/ActivityLogs/ActivityLogsEventEnum.php b/app/Enums/ActivityLogs/ActivityLogsEventEnum.php index df947592..864edaec 100644 --- a/app/Enums/ActivityLogs/ActivityLogsEventEnum.php +++ b/app/Enums/ActivityLogs/ActivityLogsEventEnum.php @@ -25,7 +25,6 @@ enum ActivityLogsEventEnum: int self::deleted->name => 'Suppression', ]; - /** * Custom added classes definition */ diff --git a/app/Enums/Arcadia/ArcadiaOrderType.php b/app/Enums/Arcadia/ArcadiaOrderType.php new file mode 100644 index 00000000..96dc70a4 --- /dev/null +++ b/app/Enums/Arcadia/ArcadiaOrderType.php @@ -0,0 +1,13 @@ +name => 'La santé des volailles', ]; - /** + /** * Get all breeding pages. * * @return array diff --git a/app/Enums/ContactFormCivility.php b/app/Enums/ContactFormCivility.php index e9da6c00..229414e2 100644 --- a/app/Enums/ContactFormCivility.php +++ b/app/Enums/ContactFormCivility.php @@ -8,7 +8,7 @@ enum ContactFormCivility: int { use BaseEnum; - case m = 1; + case m = 1; case mme = 2; /** @@ -17,7 +17,7 @@ enum ContactFormCivility: int * @phpstan-ignore-next-line */ private const LABELS = [ - self::m->name => 'M', + self::m->name => 'M', self::mme->name => 'Mme', ]; } diff --git a/app/Enums/ContactFormType.php b/app/Enums/ContactFormType.php index 2c7022ce..9c67ff10 100644 --- a/app/Enums/ContactFormType.php +++ b/app/Enums/ContactFormType.php @@ -45,13 +45,14 @@ enum ContactFormType: int sur le site.', self::others => 'Je souhaite faire une réclamation sur un jeu Le Gaulois ou je rencontre un problème sur le site.', + self::nl => '', }; } /** * Get the picto src/alt. * - * @return string + * @return array */ public function picto(): array { diff --git a/app/Enums/Mercanet/PaymentResponse.php b/app/Enums/Mercanet/PaymentResponse.php new file mode 100644 index 00000000..35ebcfe5 --- /dev/null +++ b/app/Enums/Mercanet/PaymentResponse.php @@ -0,0 +1,44 @@ +name => 'Situation normale suivie du processus normal + d\'affichage des pages de paiement.', + self::invalidIds->name => 'L’identifiant commerçant ou le contrat acquéreur sont invalides.', + self::invalidParameters->name => 'Les paramètres de la transaction sont invalides. Vérifiez les + paramètres de la requête.', + self::canceled->name => 'Transaction annulée', + self::invalidRequestFormat->name => 'Le format de la requête est invalide.', + self::securityIssue->name => 'Problème de sécurité, par ex. le sceau calculé est incorrect.', + self::transactionAlreadyExists->name => 'La transaction existe déjà.', + self::expired->name => 'Délais expiré', + self::unreachableService->name => 'Service temporairement indisponible', + self::internalIssue->name => 'Erreur interne, eg: probleme appel CURL' + ]; + //phpcs:enable +} diff --git a/app/Enums/Mercanet/RedirectionStatusCode.php b/app/Enums/Mercanet/RedirectionStatusCode.php new file mode 100644 index 00000000..d50358da --- /dev/null +++ b/app/Enums/Mercanet/RedirectionStatusCode.php @@ -0,0 +1,35 @@ +name => 'Redirection Acceptée', + self::requestError03->name => 'Problème de request code 03', + self::requestError12->name => 'Problème de request code 12', + self::requestError30->name => 'Problème de request code 30', + self::requestError34->name => 'Problème de request code 34', + self::referenceAlreadyUsed->name => 'La référence de transaction à déja été utilisée', + self::serviceDisponibiltyIssue->name => 'Problème de disponibilité du service de paiement', + self::internalIssue->name => 'Erreur interne, eg: probleme appel CURL' + ]; +} diff --git a/app/Enums/Mercanet/TransactionResponse.php b/app/Enums/Mercanet/TransactionResponse.php new file mode 100644 index 00000000..a60429d8 --- /dev/null +++ b/app/Enums/Mercanet/TransactionResponse.php @@ -0,0 +1,37 @@ +name => 'Paiement accepté', + self::refused->name => 'Paiement refusé', + self::canceled->name => 'Paiement annulé', + self::fraud->name => 'Paiement refusé pour fraude', + self::maxReached->name => 'Paiement refusé pour nombre max essais atteint', + self::technicalIssue->name => 'Paiement refusé pour problème technique', + self::duplicate->name => 'Transaction dupliquée', + self::expired->name => 'Paiement expiré', + self::internalIssue->name => 'Erreur interne, eg: probleme appel CURL' + ]; +} diff --git a/app/Enums/Club/NewsClubTypeEnum.php b/app/Enums/News/NewsClubTypeEnum.php similarity index 97% rename from app/Enums/Club/NewsClubTypeEnum.php rename to app/Enums/News/NewsClubTypeEnum.php index 126a6a32..5bebc258 100644 --- a/app/Enums/Club/NewsClubTypeEnum.php +++ b/app/Enums/News/NewsClubTypeEnum.php @@ -1,6 +1,6 @@ name => 'M', + self::mme->name => 'Mme', + ]; +} diff --git a/app/Enums/Shop/OrderStatus.php b/app/Enums/Shop/OrderStatus.php new file mode 100644 index 00000000..7e884f46 --- /dev/null +++ b/app/Enums/Shop/OrderStatus.php @@ -0,0 +1,29 @@ +name => 'En attente', + self::failed->name => 'Échec', + self::payed->name => 'Réglée', + self::waitingCheck->name => 'En attente du chèque bancaire', + self::sentCheck->name => 'Envoi du chèque bancaire', + ]; +} diff --git a/app/Enums/Shop/OrderType.php b/app/Enums/Shop/OrderType.php new file mode 100644 index 00000000..b3eca52c --- /dev/null +++ b/app/Enums/Shop/OrderType.php @@ -0,0 +1,23 @@ +name => 'Carte Bancaire', + self::check->name => 'Chèque', + ]; +} diff --git a/app/Enums/Shop/SellItemsType.php b/app/Enums/Shop/SellItemsType.php new file mode 100644 index 00000000..b650fc15 --- /dev/null +++ b/app/Enums/Shop/SellItemsType.php @@ -0,0 +1,41 @@ +name => 'Papier', + self::magnetic->name => 'Magnétique', + ]; + + /** + * Custom added color definition. + */ + private const COLOR = [ + self::paper->name => 'danger', + self::magnetic->name => 'warning', + ]; + + /** + * Get color. + * + * @return string + */ + public function color(): string + { + return self::COLOR[$this->name]; + } +} diff --git a/app/Exceptions/AbortContactMailer.php b/app/Exceptions/AbortContactMailer.php index e667a41e..ee33ba25 100644 --- a/app/Exceptions/AbortContactMailer.php +++ b/app/Exceptions/AbortContactMailer.php @@ -4,4 +4,6 @@ namespace App\Exceptions; use Exception; -class AbortContactMailer extends Exception {} +class AbortContactMailer extends Exception +{ +} diff --git a/app/Exceptions/CBPayementError.php b/app/Exceptions/CBPayementError.php new file mode 100644 index 00000000..7b6719d4 --- /dev/null +++ b/app/Exceptions/CBPayementError.php @@ -0,0 +1,9 @@ +credentials($request); // * Attempt login only using published users. diff --git a/app/Http/Controllers/Bo/NewsClubController.php b/app/Http/Controllers/Bo/NewsClubController.php index 64109f10..57691b36 100644 --- a/app/Http/Controllers/Bo/NewsClubController.php +++ b/app/Http/Controllers/Bo/NewsClubController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Bo; -use App\Enums\Club\NewsClubTypeEnum; +use App\Enums\News\NewsClubTypeEnum; use App\Http\Controllers\Controller; use App\Http\Requests\Bo\NewsClubs\StoreNewsClubRequest; use App\Http\Requests\Bo\NewsClubs\UpdateNewsClubRequest; diff --git a/app/Http/Controllers/Bo/ProductController.php b/app/Http/Controllers/Bo/ProductController.php index ac311278..86a4e882 100644 --- a/app/Http/Controllers/Bo/ProductController.php +++ b/app/Http/Controllers/Bo/ProductController.php @@ -185,7 +185,10 @@ class ProductController extends Controller public function update(UpdateProductRequest $request, Product $product): \Illuminate\Http\RedirectResponse { return DB::transaction(function () use ($request, $product) { - $product->fill(Arr::except($request->validated(), ['labels', 'productTypes', 'claims', 'nutritionnal_values'])); + $product->fill(Arr::except( + $request->validated(), + ['labels', 'productTypes', 'claims', 'nutritionnal_values'] + )); $product->labels()->sync(collect($request->validated()['labels'] ?? [])->pluck('id')); $product->productTypes()->sync(collect($request->validated()['productTypes'] ?? [])->pluck('id')); $product->claims()->sync(collect($request->validated()['claims'] ?? [])->pluck('id')); diff --git a/app/Http/Controllers/Bo/SellItemController.php b/app/Http/Controllers/Bo/SellItemController.php new file mode 100644 index 00000000..778347a7 --- /dev/null +++ b/app/Http/Controllers/Bo/SellItemController.php @@ -0,0 +1,176 @@ +authorizeResource(SellItem::class); + } + + /** + * Display a listing of the resource. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Contracts\View\View + */ + public function index(Request $request): \Illuminate\Contracts\View\View + { + /** @var \Illuminate\Database\Eloquent\Builder $query */ + $query = SellItem::query(); + + /** @var string $search Search field */ + $search = $request->search; + if ($search) { + $this->searchQuery( + $query, + $search, + null, + 'name', + ); + } + $searchFields = \implode(', ', [ + trans('validation.attributes.name'), + ]); + + /** Sort columns with a query */ + $this->sortQuery($query); + + /** Custom pagination */ + $sellItemModels = $this->paginate($query); + + return view('bo.pages.sell_items.index', compact('sellItemModels', 'search', 'searchFields')); + } + + /** + * Show the specified resource. + * + * @param \App\Models\SellItem $sellItem + * @return \Illuminate\Contracts\View\View + */ + public function show(SellItem $sellItem): \Illuminate\Contracts\View\View + { + return view('bo.pages.sell_items.show', ['sellItemModel' => $sellItem]); + } + + /** + * Show the form for creating a new resource. + * + * @param \App\Models\SellItem $sellItem + * @return \Illuminate\Contracts\View\View + */ + public function create(SellItem $sellItem): \Illuminate\Contracts\View\View + { + return view('bo.pages.sell_items.create', ['sellItemModel' => $sellItem]); + } + + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\Bo\SellItems\StoreSellItemRequest $request + * @return \Illuminate\Http\RedirectResponse + */ + public function store(StoreSellItemRequest $request): \Illuminate\Http\RedirectResponse + { + return DB::transaction(function () use ($request) { + $sellItemModel = new SellItem(); + $sellItemModel->fill($request->validated()); + + if ($sellItemModel->saveOrFail()) { + return redirect()->route('bo.sell_items.edit', $sellItemModel) + ->with('success', trans('crud.messages.has_been_created', [ + 'model' => Str::ucfirst(trans_choice('models.classes.sell_item', 1)) + ])); + } + return redirect()->back() + ->with('error', trans('crud.messages.cannot_be_created', [ + 'model' => Str::ucfirst(trans_choice('models.classes.sell_item', 1)) + ])); + }); + } + + /** + * Show the form for editing the specified resource. + * + * @param \App\Models\SellItem $sellItem + * @return \Illuminate\Contracts\View\View + */ + public function edit(SellItem $sellItem): \Illuminate\Contracts\View\View + { + return view('bo.pages.sell_items.edit', ['sellItemModel' => $sellItem]); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\Bo\SellItems\UpdateSellItemRequest $request + * @param \App\Models\SellItem $sellItem + * @return \Illuminate\Http\RedirectResponse + */ + public function update(UpdateSellItemRequest $request, SellItem $sellItem): \Illuminate\Http\RedirectResponse + { + return DB::transaction(function () use ($request, $sellItem) { + $sellItem->fill($request->validated()); + + if ($sellItem->saveOrFail()) { + return redirect()->route('bo.sell_items.edit', $sellItem) + ->with('success', trans('crud.messages.has_been_updated', [ + 'model' => Str::ucfirst(trans_choice('models.classes.sell_item', 1)) + ])); + } + return redirect()->route('bo.sell_items.edit', $sellItem) + ->with('error', trans('crud.messages.cannot_be_updated', [ + 'model' => Str::ucfirst(trans_choice('models.classes.sell_item', 1)) + ])); + }); + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Models\SellItem $sellItem + * @return \Illuminate\Http\RedirectResponse + */ + public function destroy(SellItem $sellItem): \Illuminate\Http\RedirectResponse + { + /** @var array */ + $previousQueries = []; + \parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries); + if ($sellItem->deleteOrFail()) { + return redirect()->route('bo.sell_items.index', $previousQueries) + ->with('success', trans('crud.messages.has_been_deleted', [ + 'model' => Str::ucfirst(trans_choice('models.classes.sell_item', 1)) + ])); + } + return redirect()->back() + ->with('error', trans('crud.messages.cannot_be_deleted', [ + 'model' => Str::ucfirst(trans_choice('models.classes.sell_item', 1)) + ])); + } + + /** + * Duplicate the specified resource. + * + * @param \App\Models\SellItem $sellItem + * @return \Illuminate\Contracts\View\View + */ + public function duplicate(SellItem $sellItem): \Illuminate\Contracts\View\View + { + return $this->create($sellItem->replicate()); + } +} diff --git a/app/Http/Controllers/Bo/SurveysAnswerController.php b/app/Http/Controllers/Bo/SurveysAnswerController.php index d84a9f92..ddf27f9b 100644 --- a/app/Http/Controllers/Bo/SurveysAnswerController.php +++ b/app/Http/Controllers/Bo/SurveysAnswerController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Bo; -use App\Enums\Club\NewsClubTypeEnum; +use App\Enums\News\NewsClubTypeEnum; use App\Http\Controllers\Controller; use App\Http\Requests\Bo\SurveysAnswers\StoreSurveysAnswerRequest; use App\Http\Requests\Bo\SurveysAnswers\UpdateSurveysAnswerRequest; @@ -242,10 +242,12 @@ class SurveysAnswerController extends Controller return back()->with('error', trans('crud.messages.order_not_changed')); } return DB::transaction(function () use ($request, $className, $model) { + /** @var \App\Models\NewsClub $newsClubModel */ + $newsClubModel = request()->route()->parameter('news_club'); /** @var \Illuminate\Database\Eloquent\Model|null */ $tmp = $className::query() - ->when(request()->route()->parameter('news_club'), function (Builder $q) { - $q->where('news_club_id', request()->route()->parameter('news_club')->getKey()); + ->when($newsClubModel, function (Builder $q) use ($newsClubModel) { + $q->where('news_club_id', $newsClubModel->getKey()); }) ->where('order', $request->direction ? '<' : '>', $model->getOriginal('order')) ->orderBy('order', $request->direction ? 'DESC' : 'ASC') diff --git a/app/Http/Controllers/Club/Auth/LoginController.php b/app/Http/Controllers/Club/Auth/LoginController.php index 24fbfaf1..799c62f2 100644 --- a/app/Http/Controllers/Club/Auth/LoginController.php +++ b/app/Http/Controllers/Club/Auth/LoginController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Club\Auth; use App\Http\Controllers\Controller; +use App\Lib\Utils; use App\Models\Customer; use Exception; use Illuminate\Database\Eloquent\Model; @@ -10,6 +11,7 @@ use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Session; use LDC\LDCCRM\Models\Customer as CrmCustomer; @@ -65,21 +67,43 @@ class LoginController extends Controller */ protected function attemptLogin(Request $request): bool { - $credentials = $this->credentials($request); - + $credentials = $this->credentials($request); $customerModel = Customer::query()->where('email', Arr::get($credentials, 'email'))->first(); + $crmCustomer = CrmCustomer::findWithEmail(Arr::get($credentials, 'email')); // Check database customer. if (!$customerModel) { - return false; - } + // Check crm customer. + if (!$crmCustomer) { + return false; + } + + /** @var \App\Models\Customer $customerModel */ + $customerModel = new Customer(Arr::only($crmCustomer->jsonSerialize(), ['email'])); + $customerModel->username = Utils::getUniquePseudo( + $crmCustomer->firstname, + $crmCustomer->lastname + ); + $customerModel->hashcode = $crmCustomer->hashcode; + + if (!$customerModel->saveOrFail()) { + return false; + } + } else { + // Check crm customer. + if (!$crmCustomer) { + $customerModel->delete(); + return false; + } + } //end if - // Check CRM customer. try { CrmCustomer::login(Arr::get($credentials, 'email'), Arr::get($credentials, 'password')); } catch (Exception $ex) { + Log::error($ex); return false; } + $this->guard()->loginUsingId($customerModel->getKey()); return true; } diff --git a/app/Http/Controllers/Club/Auth/VerificationController.php b/app/Http/Controllers/Club/Auth/VerificationController.php index 9812b429..a8ee8ad8 100644 --- a/app/Http/Controllers/Club/Auth/VerificationController.php +++ b/app/Http/Controllers/Club/Auth/VerificationController.php @@ -104,7 +104,7 @@ class VerificationController extends Controller } /** - * Get the post register / login redirect path. + * Get the post register/login redirect path. * * @return string */ @@ -114,7 +114,7 @@ class VerificationController extends Controller } /** - * Set Crm Customer active + * Set crm customer active. * * @param \App\Models\Customer $customer * @return void diff --git a/app/Http/Controllers/Club/DiscountController.php b/app/Http/Controllers/Club/DiscountController.php index 63cdf369..1832bf20 100644 --- a/app/Http/Controllers/Club/DiscountController.php +++ b/app/Http/Controllers/Club/DiscountController.php @@ -55,7 +55,6 @@ class DiscountController extends Controller 'Content-Type' => 'application/pdf', 'Expires' => 'Sat, 26 Jul 1997 05:00:00 GMT', 'Last-Modified' => gmdate('D, d M Y H:i:s') . ' GMT', - // For glory. 'X-Generator' => 'mPDF' ]); } @@ -254,6 +253,4 @@ class DiscountController extends Controller return false; } //end try } - - // * STATIC Fetch BR } diff --git a/app/Http/Controllers/Club/ShopController.php b/app/Http/Controllers/Club/ShopController.php index 11f19492..c9a20e15 100644 --- a/app/Http/Controllers/Club/ShopController.php +++ b/app/Http/Controllers/Club/ShopController.php @@ -2,30 +2,455 @@ namespace App\Http\Controllers\Club; +use App\Enums\Customers\Civility; +use App\Enums\Mercanet\PaymentResponse; +use App\Enums\Mercanet\RedirectionStatusCode; +use App\Enums\Mercanet\TransactionResponse; +use App\Enums\Shop\OrderStatus; +use App\Enums\Shop\OrderType; +use App\Exceptions\CBPayementError; use App\Http\Controllers\Controller; +use App\Http\Requests\Club\Shop\ShopBuyRequest; +use App\Jobs\Arcadia\SendOrderToArcadia; +use App\Jobs\Shop\SendShopMail; +use App\Lib\Mercanet\Mercanet; +use App\Models\DeliveryAddress; +use App\Models\Notification; +use App\Models\Order; +use App\Models\SellItem; +use Exception; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Database\QueryException; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Log; class ShopController extends Controller { /** - * Show index page + * Shop index. * * @return \Illuminate\Contracts\View\View */ public function index(): \Illuminate\Contracts\View\View { - return view('club.pages.shop.index'); + $sellItemModels = SellItem::all(); + return view('club.pages.shop.index', compact('sellItemModels')); } /** - * Show index page + * Ajax service to validate transaction details. * - * @return \Illuminate\Contracts\View\View + * @param \App\Http\Requests\Club\Shop\ShopBuyRequest $request + * @return \Illuminate\Http\JsonResponse + * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh */ - public function create(): \Illuminate\Contracts\View\View + public function validateBuy(ShopBuyRequest $request): \Illuminate\Http\JsonResponse { - /** @var \App\Models\Customer $customerModel */ - $customerModel = auth('club')->user(); - $crmCustomer = $customerModel->api_data; - return view('club.pages.shop.recap', compact('crmCustomer')); + // phpcs:enable + /** @var \App\Models\Customer */ + $customerModel = auth('club')->user(); + $items = []; + $orderTotalPrice = 0; + foreach ($request->cart as $item) { + $sellItem = SellItem::query()->where('id', $item['item']['id'])->firstOrFail(); + $items[] = [ + 'id' => $sellItem->id, + 'name' => $sellItem->name, + 'quantity' => $item['count'], + 'unitPrice' => $sellItem->price, + 'totalPrice' => $item['count'] * $sellItem->price, + 'subject' => $sellItem->arcadia_id + ]; + $orderTotalPrice += $item['count'] * $sellItem->price; + } + + // * Pseudo mutex let database fail (unique ref) to check if order has already been processed + try { + $order = new Order(); + $order->ref = str_replace('-', '', $request->ref); + $order->civility = Civility::make(intval($request->civility)); + $order->payementType = OrderType::make(intval($request->payementMethod)); + $order->status = OrderStatus::waiting; + $order->customer_id = $customerModel->getKey(); + $order->fill($request->coords); + $order->saveOrFail(); + } catch (QueryException $e) { + $order = Order::query()->where('ref', str_replace('-', '', $request->ref))->firstOrFail(); + if ($order->payementType->equals(OrderType::check)) { + return response()->json(['redirect' => route( + 'club.shop.validated', + compact('order') + )]); + } + // FIXME: uncomment when mercanet cb is ready. + // if ($order->payementType->equals(OrderType::cb)) { + // return response()->json(['redirect' => route( + // 'club.shop.payementCB', + // compact('order') + // )]); + // } + abort(500, sprintf('Order already exists but type is unkown %d', $order->id)); + } //end try + + $order->customer()->associate($customerModel); + + foreach ($items as $item) { + $order->sellItems()->withTimestamps()->attach($item['id'], ['quantity' => $item['quantity']]); + } + $order->saveOrFail(); + + if ($request->shipping) { + $shipping = new DeliveryAddress(); + $shipping->fill($request->shipping); + $shipping->order()->associate($order); + $shipping->saveOrFail(); + } + + switch ($request->payementMethod) { + // FIXME: uncomment when mercanet cb is ready. + // case OrderType::cb->value(): + // \session()->put(sprintf('shop.order.%d.time', $order->id), \time()); + // \session()->save(); + // return response()->json(['redirect' => route('club.shop.payementCB', compact('order'))]); + case OrderType::check->value(): + try { + \session()->put(sprintf('shop.order.%d.time', $order->id), \time()); + \session()->save(); + SendOrderToArcadia::dispatch($order); + SendShopMail::dispatch($order); + $order->status = OrderStatus::waitingCheck; + $order->saveOrFail(); + } catch (Exception $e) { + report($e); + Log::critical(sprintf( + 'Could not end customer transaction (save and send mail) in shop for order %d, %s', + $order->id, + $e->getMessage() + )); + } finally { + return response()->json(['redirect' => route('club.shop.validated', compact('order'))]); + } + break; + // * not used + default: + abort(422); + } //end switch + } + + /** + * Redirection After Ajax validate. + * + * @param \App\Models\Order $order + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View + */ + public function validatedBuy(Order $order): \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View + { + Notification::getNotifications(); + $time = session()->get(sprintf('shop.order.%d.time', $order->id), 0); + if (!($order->status->equals(OrderStatus::payed) || $order->status->equals(OrderStatus::waitingCheck))) { + return response()->redirectToRoute('club.shop.index'); + } + // 4 minutes + if (($time + (60 * 4)) < \time()) { + return response()->redirectToRoute('club.shop.index'); + } + + return view('club.pages.shop.validated'); + } + + /** + * Pay with CB. + * + * @param \App\Models\Order $order + * @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse + * @throws \App\Exceptions\CBPayementError Error response from Mercanet Lib. + * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh + */ + public function payWithCb(Order $order): \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse + { + // phpcs:enable + Notification::getNotifications(); + try { + $time = session()->get(sprintf('shop.order.%d.time', $order->id), 0); + if ($order->status->equals(OrderStatus::failed)) { + return view('club.pages.shop.error') + ->with('error', trans('Commande annulée, veuillez réessayer')); + } + + // 4 minutes + if (($time + (60 * 4)) < \time()) { + return view('club.pages.shop.error') + ->with('error', trans('Délai d\'attente expiré, veuillez réessayer')); + } + + $totalToPay = $order->sellItems->map(function ($sellItem) { + return $sellItem->pivot->quantity * $sellItem->price; + })->sum(); + + // * Convert total in cents + $totalToPay = (int)(\number_format((float)$totalToPay, 2) * 100); + + $mercanet = $this->getMercanet($order); + + $payment = $mercanet->QuickPayment($totalToPay, $order->ref); + + switch ($payment->code) { + case RedirectionStatusCode::redirect->value(): + return view('club.pages.shop.redirectMercanet', [ + 'url' => $payment->url, + 'data' => $payment->data, + 'version' => $payment->version + ]); + case RedirectionStatusCode::referenceAlreadyUsed: + return response()->redirectToRoute('club.shop.index'); + case RedirectionStatusCode::requestError03->value(): + case RedirectionStatusCode::requestError12->value(): + case RedirectionStatusCode::requestError30->value(): + case RedirectionStatusCode::requestError34->value(): + case RedirectionStatusCode::serviceDisponibiltyIssue->value(): + $order->status = OrderStatus::failed; + $order->save(); + throw new CBPayementError(sprintf( + "Error response from Mercanet Lib : %s\n %s", + RedirectionStatusCode::make(intval($payment->code))->label(), + json_encode($payment) + )); + default: + $order->status = OrderStatus::failed; + $order->save(); + throw new CBPayementError(sprintf( + 'Un handled response from Mercanet Lib %s', + json_encode($payment) + )); + } //end switch + } catch (ModelNotFoundException | CBPayementError $e) { + report($e); + Log::critical(sprintf( + 'Could not end customer transaction in boutique for order %d, %s', + $order->id, + $e->getMessage() + )); + return view('club.pages.shop.error') + ->with('error', trans('La commande que vous venez de passer n\'a pu aboutir, veuillez réessayer')); + } //end try + } + + /** + * Pas de validate ou custom request sur la réponse de mercanet, + * c'est la Lib mercanet qui fait. + * + * @param \Illuminate\Http\Request $request + * @param \App\Models\Order $order + * @return \Illuminate\Http\Response|\Illuminate\Contracts\Routing\ResponseFactory + * @throws \App\Exceptions\CBPayementError Data were altered from response. + * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh + */ + public function confirmCbFromMercanet( + Request $request, + Order $order + ): \Illuminate\Http\Response|\Illuminate\Contracts\Routing\ResponseFactory { + // phpcs:enable + try { + \extract($this->getMercanetData($request)); + + /** @var \App\Models\Order */ + $order = Order::query()->where('ref', $ref)->firstOrFail(); + + $mercanet = $this->getMercanet($order); + $mercanet->setResponse($request->post()); + + if (!$mercanet->isValid()) { + $order->status = OrderStatus::failed; + $order->save(); + throw new CBPayementError('Data were altered from response'); + } + + switch ($responseCode) { + case TransactionResponse::accepted: + $order->status = OrderStatus::payed; + $order->save(); + SendOrderToArcadia::dispatch($order); + SendShopMail::dispatch($order); + break; + case TransactionResponse::canceled: + case TransactionResponse::expired: + case TransactionResponse::refused: + case TransactionResponse::fraud: + case TransactionResponse::duplicate: + case TransactionResponse::maxReached: + if ( + !$order->confirmMailSent and + !$order->status->equals(OrderStatus::payed) + ) { + $order->status = OrderStatus::failed; + $order->save(); + } + break; + case TransactionResponse::technicalIssue: + throw new CBPayementError(sprintf( + 'Error from Mercanet Server %s on ref %s, %s', + $responseCode, + $ref, + json_encode($returnDatas) + )); + default: + // * ERROR + if ( + !$order->confirmMailSent and + !$order->status->equals(OrderStatus::payed) + ) { + $order->status = OrderStatus::failed; + $order->save(); + } + throw new CBPayementError(sprintf( + 'Un handled response from Mercanet Server %s on ref %s', + $responseCode, + $ref, + json_encode($returnDatas) + )); + } //end switch + } catch (ModelNotFoundException | CBPayementError | \Exception $e) { + report($e); + Log::critical(sprintf( + 'Could not end customer transaction in boutique for order %d, %s', + $order->id, + $e->getMessage() + )); + } finally { + return response('', 200); + } //end try + } + + /** + * + * Pas de validate ou custom request sur la réponse de mercanet, + * c'est la Lib mercanet qui fait. + * + * @param Request $request + * @param Order $order + * @return \Illuminate\Contracts\View\View + * @throws \App\Exceptions\CBPayementError Data were altered from response. + * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh + */ + public function confirmCb(Request $request, Order $order): \Illuminate\Contracts\View\View + { + // phpcs:enable + Notification::getNotifications(); + try { + if ($order->status->equals(OrderStatus::failed)) { + return view('club.pages.shop.error'); + } + if ($order->status->equals(OrderStatus::payed)) { + return view('club.pages.shop.validated'); + } + + $mercanet = $this->getMercanet($order); + $mercanet->setResponse($request->post()); + + if (!$mercanet->isValid()) { + try { + $order->status = OrderStatus::failed; + $order->saveOrFail(); + } finally { + throw new CBPayementError("Data were altered from response"); + } + } + + \extract($this->getMercanetData($request)); + + switch ($responseCode) { + case PaymentResponse::ok: + try { + $status = $order->status; + $order->status = OrderStatus::payed; + $order->saveOrFail(); + + if ($status->equals(OrderStatus::waiting)) { + SendOrderToArcadia::dispatch($order); + SendShopMail::dispatch($order); + } + } catch (Exception $e) { + report($e); + Log::critical(sprintf( + 'Could not end customer transaction (save and send mail) in boutique for order %d, %s', + $order->id, + $e->getMessage() + )); + } finally { + return view('club.pages.shop.validated'); + } + case PaymentResponse::canceled: + $order->status = OrderStatus::failed; + $order->saveOrFail(); + return view('club.pages.shop.error'); + case PaymentResponse::invalidIds: + case PaymentResponse::invalidRequestFormat: + case PaymentResponse::invalidParameters: + case PaymentResponse::unreachableService: + case PaymentResponse::securityIssue: + case PaymentResponse::transactionAlreadyExists: + case PaymentResponse::expired: + case PaymentResponse::internalIssue: + $order->status = OrderStatus::failed; + $order->saveOrFail(); + throw new CBPayementError(sprintf( + "Error response from Mercanet Lib : %s\n %s", + PaymentResponse::make($responseCode)->label, + json_encode($returnDatas) + )); + default: + $order->status = OrderStatus::failed; + $order->save(); + throw new CBPayementError(sprintf( + 'Un handled response from Mercanet Lib %s', + json_encode($returnDatas) + )); + } //end switch + } catch (ModelNotFoundException | CBPayementError $e) { + report($e); + Log::critical(sprintf( + 'Could not end customer transaction in boutique for order %d, %s', + $order->id, + $e->getMessage() + )); + return view('club.pages.shop.error'); + } //end try + } + + /** + * Get initialized Mercanet object. + * + * @param \App\Models\Order $order + * @return \App\Lib\Mercanet\Mercanet + */ + private function getMercanet(Order $order): \App\Lib\Mercanet\Mercanet + { + return new Mercanet( + config('mercanet.secret'), + config('mercanet.merchantId'), + config('mercanet.env'), + route('club.shop.reglement.payementConfirmCB', $order), + route('club.shop.reglement.payementConfirmCBServ', $order) + ); + } + + /** + * Get Mercanet ref and responseCode for request. + * + * @param \Illuminate\Http\Request $request + * @return array array['ref' => 'value', 'responseCode' => 'value'] + */ + private function getMercanetData(Request $request): array + { + $formDatas = []; + $returnDatas = explode("|", $request->post('Data')); + foreach ($returnDatas as $item) { + $tmp = explode("=", $item); + $formDatas[$tmp[0]] = $tmp[1]; + } + + $ref = $formDatas['transactionReference']; + $responseCode = $formDatas['responseCode']; + return compact('ref', 'responseCode', 'returnDatas'); } } diff --git a/app/Http/Controllers/Fo/BreederController.php b/app/Http/Controllers/Fo/BreederController.php index c91654e2..29b4bf8c 100644 --- a/app/Http/Controllers/Fo/BreederController.php +++ b/app/Http/Controllers/Fo/BreederController.php @@ -26,10 +26,7 @@ class BreederController extends Controller return File::json(base_path('/public/departments.geojson')); } ); - - return response()->json( - [$departmentGeoJson] - ); + return response()->json([$departmentGeoJson]); } /** @@ -39,7 +36,7 @@ class BreederController extends Controller */ public function getBreederCountPerDepartment(): \Illuminate\Http\JsonResponse { - // TODO: mettre à jour le cache lors de l'édition/ajout/suppression d'un éleveur + // TODO: mettre à jour le cache lors de l'édition/ajout/suppression d'un éleveur. $breederCountPerDepartment = Cache::rememberForever( 'breederCountPerDepartment', function () { @@ -50,10 +47,7 @@ class BreederController extends Controller ->get()->toArray(); } ); - - return response()->json( - $breederCountPerDepartment - ); + return response()->json($breederCountPerDepartment); } /** @@ -65,7 +59,7 @@ class BreederController extends Controller public function getDepartmentBreeders(Request $request): \Illuminate\Http\JsonResponse { // TODO: checker si le departement existe. - // TODO: mettre à jour le cache lors de l'édition/ajout/suppression d'un éleveur + // TODO: mettre à jour le cache lors de l'édition/ajout/suppression d'un éleveur. $breeders = Cache::rememberForever( sprintf('departmentBreeders%s', $request->DEPARTMENT), function () use ($request) { @@ -76,10 +70,7 @@ class BreederController extends Controller ->get()->toArray(); } ); - - return response()->json( - $breeders - ); + return response()->json($breeders); } /** diff --git a/app/Http/Controllers/Fo/ContactController.php b/app/Http/Controllers/Fo/ContactController.php index 7aefe0e1..e6ce13b6 100644 --- a/app/Http/Controllers/Fo/ContactController.php +++ b/app/Http/Controllers/Fo/ContactController.php @@ -3,21 +3,16 @@ namespace App\Http\Controllers\Fo; use App\Enums\ContactFormType; -use App\Enums\Listen\ObjectEnum; use App\Enums\StaticPageType; use App\Exceptions\AbortContactMailer; use App\Http\Controllers\Controller; use App\Http\Requests\Fo\ContactFormRequest; use App\Jobs\Mails\SendArcadiaMail; use App\Jobs\Mails\SendPasserelleMail; -use App\Lib\Helpers\FileStorageHelper; use App\Models\MailContactData; use App\Models\StaticPage; -use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Storage; class ContactController extends Controller @@ -48,11 +43,11 @@ class ContactController extends Controller // Send to Passerelle if it is a compalin about a product. if ($request->type == ContactFormType::complain->value) { - $joinFilePath = null; - $joinFile = ''; + $joinFilePath = null; + $joinFile = ''; $uniqIdDirectory = \uniqid(); if ($request->file('joinFile')) { - $joinFile = Storage::disk('local')->put( + $joinFile = Storage::disk('local')->put( sprintf('public/tmp/%s', $uniqIdDirectory), $request->file('joinFile') ); @@ -67,8 +62,11 @@ class ContactController extends Controller return back()->with('success', __('Votre message a été envoyé avec succès')); } catch (AbortContactMailer $e) { Log::error('Fail to build mail from complain page ', ['exception' => $e]); - } - return back()->withInput()->with('error', 'Une erreur est survenue durant l\'envoi de votre message. Veuillez réessayer ultèrieurement.'); + } //end try + return back()->withInput()->with( + 'error', + 'Une erreur est survenue durant l\'envoi de votre message. Veuillez réessayer ultérieurement.' + ); }); } } diff --git a/app/Http/Controllers/Fo/ExportController.php b/app/Http/Controllers/Fo/ExportController.php index ab2495c2..4a37b67f 100644 --- a/app/Http/Controllers/Fo/ExportController.php +++ b/app/Http/Controllers/Fo/ExportController.php @@ -9,7 +9,6 @@ class ExportController extends Controller /** * Contact page. * - * @param \App\Models\StaticPage $staticPageModel * @return \Illuminate\Contracts\View\View */ public function nl(): \Illuminate\Contracts\View\View diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index b0e39a22..81ea3e18 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -70,7 +70,7 @@ class Kernel extends HttpKernel 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'signed' => \App\Http\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, - 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class, + 'logoutusers' => \App\Http\Middleware\LogoutUnpublishedUsers::class, ]; } diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 74bb08ff..74241783 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -18,7 +18,7 @@ class Authenticate extends Middleware { $logins = [ 'backend' => route('bo.login'), - 'club' => route('club.login') + 'club' => route('club.login') ]; if (!$request->expectsJson()) { // * Notify must login @@ -34,7 +34,6 @@ class Authenticate extends Middleware } return route('login'); } - return null; } } diff --git a/app/Http/Middleware/EnsureEmailIsVerified.php b/app/Http/Middleware/EnsureEmailIsVerified.php new file mode 100644 index 00000000..b300dea4 --- /dev/null +++ b/app/Http/Middleware/EnsureEmailIsVerified.php @@ -0,0 +1,60 @@ +user(); + + if ($customerModel instanceof MustVerifyEmail) { + $crmCustomer = CrmCustomer::findWithEmail($customerModel->email); + // Check there is a crm customer. + if (!$crmCustomer) { + $customerModel->delete(); + } else { + // Check the status of crm customer & customer model. + if ($crmCustomer->status !== $customerModel->hasVerifiedEmail()) { + $customerModel->email_verified_at = $crmCustomer->status ? Carbon::now() : null; + $customerModel->saveOrFail(); + } + if (!$crmCustomer->status && !$customerModel->hasVerifiedEmail()) { + return $request->expectsJson() + ? abort(403, 'Your email address is not verified.') + : Redirect::guest(URL::route($redirectToRoute ?: 'club.verification.notice')); + } + } + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/HtmlSanitizer.php b/app/Http/Middleware/HtmlSanitizer.php index 857ca84f..c7324fd5 100644 --- a/app/Http/Middleware/HtmlSanitizer.php +++ b/app/Http/Middleware/HtmlSanitizer.php @@ -4,7 +4,6 @@ namespace App\Http\Middleware; use App\Lib\Helpers\ToolboxHelper; use Illuminate\Http\Request; -use Symfony\Component\HttpFoundation\Response; class HtmlSanitizer { @@ -16,8 +15,11 @@ class HtmlSanitizer * @param string|array ...$params * @return \Symfony\Component\HttpFoundation\Response */ - public function handle(Request $request, \Closure $next, string|array ...$params): Response - { + public function handle( + Request $request, + \Closure $next, + string|array ...$params + ): \Symfony\Component\HttpFoundation\Response { $inputs = $request->all(); /** @var callable|null */ $cleanInput = null; diff --git a/app/Http/Middleware/LogoutUnpublishedUsers.php b/app/Http/Middleware/LogoutUnpublishedUsers.php new file mode 100644 index 00000000..ea46c1ed --- /dev/null +++ b/app/Http/Middleware/LogoutUnpublishedUsers.php @@ -0,0 +1,28 @@ +published) { + Auth::logout(); + return redirect()->route('bo.login')->with('warning', trans('auth.unpublished_user')); + } + return $next($request); + } +} diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index 864e8e64..697c2a91 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -3,25 +3,26 @@ namespace App\Http\Middleware; use Illuminate\Http\Request; -use Symfony\Component\HttpFoundation\Response; class RedirectIfAuthenticated { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next - * @param string|null ...$guards + * @param \Illuminate\Http\Request $request + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + * @param string|null ...$guards * @return \Symfony\Component\HttpFoundation\Response * @throws \RuntimeException If guard is unhandled. * @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint */ - public function handle(Request $request, \Closure $next, ...$guards): Response - { + public function handle( + Request $request, + \Closure $next, + string|null ...$guards + ): \Symfony\Component\HttpFoundation\Response { // phpcs:enable phpcs:disable $guards = empty($guards) ? [null] : $guards; - foreach ($guards as $guard) { if (auth($guard)->check()) { switch ($guard) { @@ -37,7 +38,6 @@ class RedirectIfAuthenticated } } } - return $next($request); } } diff --git a/app/Http/Middleware/SecureHeaders.php b/app/Http/Middleware/SecureHeaders.php index e2f3e887..4fc3de4a 100644 --- a/app/Http/Middleware/SecureHeaders.php +++ b/app/Http/Middleware/SecureHeaders.php @@ -2,12 +2,10 @@ namespace App\Http\Middleware; -use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\Vite; -use Symfony\Component\HttpFoundation\Response; class SecureHeaders { @@ -26,14 +24,12 @@ class SecureHeaders /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + * @param \Illuminate\Http\Request $request + * @param \Closure $next * @return \Symfony\Component\HttpFoundation\Response - * @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint */ - public function handle(Request $request, Closure $next): Response + public function handle(Request $request, \Closure $next): \Symfony\Component\HttpFoundation\Response { - // phpcs:enable // * For Vite DEV $host = \parse_url(\config('app.url'), \PHP_URL_HOST); View::share('nonce', $nonce = Vite::useCspNonce()); @@ -105,7 +101,6 @@ class SecureHeaders config('app.debug') ? "https://{$host}:4443" : '', ]) . ';' ]; - $response->headers->set( 'Content-Security-Policy', implode(' ', $cspPolicies) . $reportUri @@ -115,7 +110,7 @@ class SecureHeaders } /** - * Remove unwanted headers + * Remove unwanted headers. * * @param array $headerList * @return void diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index 608f711a..8bf70029 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -21,8 +21,7 @@ class TrustProxies extends Middleware * * @var integer */ - protected $headers = - Request::HEADER_X_FORWARDED_FOR | + protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | diff --git a/app/Http/Middleware/XssSanitizer.php b/app/Http/Middleware/XssSanitizer.php new file mode 100644 index 00000000..a034761a --- /dev/null +++ b/app/Http/Middleware/XssSanitizer.php @@ -0,0 +1,35 @@ +all(); + array_walk_recursive($inputs, function (&$input, $name) use ($params) { + if (in_array($name, $params)) { + $input = clean($input); + $input = ToolboxHelper::assertNoInternalUrls($name, $input); + } else { + $input = strip_tags($input); + } + }); + $request->merge($inputs); + return $next($request); + } +} diff --git a/app/Http/Requests/Bo/NewsClubs/StoreNewsClubRequest.php b/app/Http/Requests/Bo/NewsClubs/StoreNewsClubRequest.php index 05283058..ebc425ff 100644 --- a/app/Http/Requests/Bo/NewsClubs/StoreNewsClubRequest.php +++ b/app/Http/Requests/Bo/NewsClubs/StoreNewsClubRequest.php @@ -2,7 +2,7 @@ namespace App\Http\Requests\Bo\NewsClubs; -use App\Enums\Club\NewsClubTypeEnum; +use App\Enums\News\NewsClubTypeEnum; use App\Models\NewsClub; use App\Rules\WysiwygLength; use App\Traits\Requests\HasPicture; diff --git a/app/Http/Requests/Bo/NewsClubs/UpdateNewsClubRequest.php b/app/Http/Requests/Bo/NewsClubs/UpdateNewsClubRequest.php index d3d919f2..91f2acce 100644 --- a/app/Http/Requests/Bo/NewsClubs/UpdateNewsClubRequest.php +++ b/app/Http/Requests/Bo/NewsClubs/UpdateNewsClubRequest.php @@ -2,7 +2,7 @@ namespace App\Http\Requests\Bo\NewsClubs; -use App\Enums\Club\NewsClubTypeEnum; +use App\Enums\News\NewsClubTypeEnum; use Illuminate\Support\Facades\Gate; class UpdateNewsClubRequest extends StoreNewsClubRequest diff --git a/app/Http/Requests/Bo/SellItems/StoreSellItemRequest.php b/app/Http/Requests/Bo/SellItems/StoreSellItemRequest.php new file mode 100644 index 00000000..03bd5b0c --- /dev/null +++ b/app/Http/Requests/Bo/SellItems/StoreSellItemRequest.php @@ -0,0 +1,76 @@ +mergePicture('picture'); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + $rules = [ + 'name' => 'required|string|between:3,255', + 'type' => ['required', new Enum(SellItemsType::class)], + 'price' => 'required|numeric|between:0.1,100', + 'picture_alt' => 'required|string|between:3,255', + 'picture_title' => 'required|string|between:3,255', + 'arcadia_id' => 'required|integer|between:0,1000', + 'arcadia_weight' => 'required|integer|between:0,100', + ]; + return \array_merge( + $rules, + $this->pictureRules('picture', 450, 450, 450, 450), + ); + } + + /** + * Get custom attributes for validator errors. + * + * @return array + */ + public function attributes(): array + { + return [ + 'name' => trans('validation.attributes.name'), + 'type' => trans('validation.custom.type'), + 'price' => trans('validation.attributes.price'), + 'picture' => trans('validation.attributes.image'), + 'picture_alt' => trans('validation.custom.attribute_alt'), + 'picture_title' => trans('validation.custom.attribute_title'), + 'arcadia_id' => trans('validation.custom.arcadia_id'), + 'arcadia_weight' => trans('validation.custom.arcadia_weight'), + ]; + } +} diff --git a/app/Http/Requests/Bo/SellItems/UpdateSellItemRequest.php b/app/Http/Requests/Bo/SellItems/UpdateSellItemRequest.php new file mode 100644 index 00000000..ec692479 --- /dev/null +++ b/app/Http/Requests/Bo/SellItems/UpdateSellItemRequest.php @@ -0,0 +1,18 @@ +route('sell_item')); + } +} diff --git a/app/Http/Requests/Club/Bourse/CustomerCollectionRequest.php b/app/Http/Requests/Club/Bourse/CustomerCollectionRequest.php index c03252dd..228419a1 100644 --- a/app/Http/Requests/Club/Bourse/CustomerCollectionRequest.php +++ b/app/Http/Requests/Club/Bourse/CustomerCollectionRequest.php @@ -49,7 +49,7 @@ abstract class CustomerCollectionRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { return [ 'state' => [ diff --git a/app/Http/Requests/Club/Bourse/SaveCustomerCollectionRequest.php b/app/Http/Requests/Club/Bourse/SaveCustomerCollectionRequest.php index 2dccbbb7..9a0a676b 100644 --- a/app/Http/Requests/Club/Bourse/SaveCustomerCollectionRequest.php +++ b/app/Http/Requests/Club/Bourse/SaveCustomerCollectionRequest.php @@ -12,7 +12,7 @@ class SaveCustomerCollectionRequest extends CustomerCollectionRequest * * @return array */ - public function rules() + public function rules(): array { $idEnumRule = ''; return array_merge( diff --git a/app/Http/Requests/Club/Bourse/SearchCustomerCollectionRequest.php b/app/Http/Requests/Club/Bourse/SearchCustomerCollectionRequest.php index f6c5f3f7..c8f7f351 100644 --- a/app/Http/Requests/Club/Bourse/SearchCustomerCollectionRequest.php +++ b/app/Http/Requests/Club/Bourse/SearchCustomerCollectionRequest.php @@ -12,7 +12,7 @@ class SearchCustomerCollectionRequest extends CustomerCollectionRequest public function prepareForValidation(): void { $this->merge([ - 'pseudo' => $this->string('pseudo'), + 'pseudo' => $this->string('pseudo'), 'departaimant' => $this->string('departaimant') ]); parent::prepareForValidation(); @@ -23,14 +23,14 @@ class SearchCustomerCollectionRequest extends CustomerCollectionRequest * * @return array */ - public function rules() + public function rules(): array { return array_merge( parent::rules(), [ - 'pseudo' => 'nullable', + 'pseudo' => 'nullable', 'departaimant' => 'nullable', - 'page' => 'required|numeric|min:1' + 'page' => 'required|numeric|min:1' ] ); } diff --git a/app/Http/Requests/Club/Customers/StoreCustomerRequest.php b/app/Http/Requests/Club/Customers/StoreCustomerRequest.php index 4db8ec96..a167a761 100644 --- a/app/Http/Requests/Club/Customers/StoreCustomerRequest.php +++ b/app/Http/Requests/Club/Customers/StoreCustomerRequest.php @@ -23,7 +23,7 @@ class StoreCustomerRequest extends FormRequest * * @return boolean */ - public function authorize() + public function authorize(): bool { return Auth::guard('club')->check(); } @@ -33,7 +33,7 @@ class StoreCustomerRequest extends FormRequest * * @return void */ - public function prepareForValidation() + public function prepareForValidation(): void { try { $this->currentCountry = Country::make(\intval($this->country)); @@ -52,7 +52,7 @@ class StoreCustomerRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { $countryList = implode(',', Country::getValidIsoAplha2Codes()); return [ @@ -97,7 +97,7 @@ class StoreCustomerRequest extends FormRequest * * @return array */ - public function attributes() + public function attributes(): array { return [ 'lastname' => trans('validation.attributes.first_name'), diff --git a/app/Http/Requests/Club/Customers/UpdateCustomerRequest.php b/app/Http/Requests/Club/Customers/UpdateCustomerRequest.php index 09eb211b..3ca3382b 100644 --- a/app/Http/Requests/Club/Customers/UpdateCustomerRequest.php +++ b/app/Http/Requests/Club/Customers/UpdateCustomerRequest.php @@ -23,7 +23,7 @@ class UpdateCustomerRequest extends StoreCustomerRequest * * @return void */ - public function prepareForValidation() + public function prepareForValidation(): void { parent::prepareForValidation(); $this->merge([ diff --git a/app/Http/Requests/Club/Customers/UpdatePasswordRequest.php b/app/Http/Requests/Club/Customers/UpdatePasswordRequest.php index 9b8abdfb..33304fa6 100644 --- a/app/Http/Requests/Club/Customers/UpdatePasswordRequest.php +++ b/app/Http/Requests/Club/Customers/UpdatePasswordRequest.php @@ -13,7 +13,7 @@ class UpdatePasswordRequest extends FormRequest * * @return boolean */ - public function authorize() + public function authorize(): bool { return auth('club')->check(); } @@ -23,7 +23,7 @@ class UpdatePasswordRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { /** @var \LDC\LDCCRM\Models\Customer $crmCustomer */ $crmCustomer = \auth('club')->user()->api_data; @@ -44,7 +44,7 @@ class UpdatePasswordRequest extends FormRequest * * @return array */ - public function attributes() + public function attributes(): array { return [ 'old_password' => trans('validation.custom.old_password'), diff --git a/app/Http/Requests/Club/Messages/BlockCustomerRequest.php b/app/Http/Requests/Club/Messages/BlockCustomerRequest.php index e5ded674..33bff942 100644 --- a/app/Http/Requests/Club/Messages/BlockCustomerRequest.php +++ b/app/Http/Requests/Club/Messages/BlockCustomerRequest.php @@ -12,7 +12,7 @@ class BlockCustomerRequest extends FormRequest * * @return boolean */ - public function authorize() + public function authorize(): bool { return Auth::guard('club')->check(); } @@ -22,7 +22,7 @@ class BlockCustomerRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { return [ 'username' => 'required|exists:customers,username' diff --git a/app/Http/Requests/Club/Messages/DeleteMessagesRequest.php b/app/Http/Requests/Club/Messages/DeleteMessagesRequest.php index 76b35d6a..bd949b2e 100644 --- a/app/Http/Requests/Club/Messages/DeleteMessagesRequest.php +++ b/app/Http/Requests/Club/Messages/DeleteMessagesRequest.php @@ -14,7 +14,7 @@ class DeleteMessagesRequest extends FormRequest * * @return boolean */ - public function authorize() + public function authorize(): bool { return Auth::guard('club')->check(); } @@ -24,7 +24,7 @@ class DeleteMessagesRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { return [ 'message' => [ diff --git a/app/Http/Requests/Club/Messages/MarkAsReadRequest.php b/app/Http/Requests/Club/Messages/MarkAsReadRequest.php index 2ecd7703..68afb837 100644 --- a/app/Http/Requests/Club/Messages/MarkAsReadRequest.php +++ b/app/Http/Requests/Club/Messages/MarkAsReadRequest.php @@ -24,7 +24,7 @@ class MarkAsReadRequest extends FormRequest public function rules(): array { return [ - 'messageList' => 'required|array', + 'messageList' => 'required|array', 'messageList.*' => 'numeric' ]; } diff --git a/app/Http/Requests/Club/Messages/SendMessageRequest.php b/app/Http/Requests/Club/Messages/SendMessageRequest.php index e268ea75..4fe2e09c 100644 --- a/app/Http/Requests/Club/Messages/SendMessageRequest.php +++ b/app/Http/Requests/Club/Messages/SendMessageRequest.php @@ -16,7 +16,7 @@ class SendMessageRequest extends FormRequest * * @return boolean */ - public function authorize() + public function authorize(): bool { return Auth::guard('club')->check(); } @@ -26,11 +26,11 @@ class SendMessageRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { return [ 'message' => 'required|string|max:255|min:5', - 'to' => [ + 'to' => [ 'required', Rule::exists('customers', 'username'), // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed @@ -40,17 +40,17 @@ class SendMessageRequest extends FormRequest $customer = Customer::query()->where('username', $value)->firstOrFail(); if ( BlockList::query() - ->whereHas('customer', fn (Builder $q) => $q->where('id', $customer->id)) - ->whereHas('blocked', fn (Builder $q) => $q->where('id', $authCustomer->id)) - ->exists() + ->whereHas('customer', fn (Builder $q) => $q->where('id', $customer->id)) + ->whereHas('blocked', fn (Builder $q) => $q->where('id', $authCustomer->id)) + ->exists() ) { $fail(trans('Vous ne pouvez pas envoyer de message à cet utilisateur')); } if ( BlockList::query() - ->whereHas('customer', fn (Builder $q) => $q->where('id', $authCustomer->id)) - ->whereHas('blocked', fn (Builder $q) => $q->where('id', $customer->id)) - ->exists() + ->whereHas('customer', fn (Builder $q) => $q->where('id', $authCustomer->id)) + ->whereHas('blocked', fn (Builder $q) => $q->where('id', $customer->id)) + ->exists() ) { $fail(trans('Vous avez bloqué cet utilisateur')); } diff --git a/app/Http/Requests/Club/Shop/ShopBuyRequest.php b/app/Http/Requests/Club/Shop/ShopBuyRequest.php new file mode 100644 index 00000000..2b7b2f21 --- /dev/null +++ b/app/Http/Requests/Club/Shop/ShopBuyRequest.php @@ -0,0 +1,110 @@ +ajax() ? true : false; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + return [ + 'ref' => 'required|string|uuid|unique:orders,ref', + 'coords' => 'required|array', + 'coords.civility' => ['required', new Enum(ContactFormCivility::class)], + 'coords.firstname' => 'required|string|between:3,255', + 'coords.lastname' => 'required|string|between:3,255', + 'coords.address' => 'required|string|between:3,255', + 'coords.postcode' => 'required|postal_code:FR', + 'coords.city' => 'required|string|between:3,255', + 'coords.phone' => 'nullable|phone:FR|max:255', + 'coords.email' => 'required|email:rfc,strict,dns,spoof,filter', + 'shipping' => 'sometimes|array', + 'shipping.civility' => ['required_with:shipping', new Enum(ContactFormCivility::class)], + 'shipping.firstname' => 'required_with:shipping|string|between:3,255', + 'shipping.lastname' => 'required_with:shipping|string|between:3,255', + 'shipping.address' => 'required_with:shipping|string|between:3,255', + 'shipping.postcode' => 'required_with:shipping|postal_code:FR', + 'shipping.city' => 'required_with:shipping|string|between:3,255', + 'shipping.phone' => 'nullable|phone:FR|max:255', + 'cart' => 'required|array', + 'cart.*' => 'required|distinct|min:1', + 'cart.*.count' => 'required|numeric|min:1', + 'cart.*.item.id' => 'required|numeric|exists:sell_items,id', + 'payementMethod' => ['required', new Enum(OrderType::class)] + ]; + } + + /** + * Get custom attributes for validator errors. + * + * @return array + */ + public function attributes(): array + { + return [ + 'coords' => trans('vos coordonnées'), + 'coords.civility' => trans('civilité dans vos coordonnées'), + 'coords.firstname' => trans('prénom dans vos coordonnées'), + 'coords.lastname' => trans('nom dans vos coordonnées'), + 'coords.address' => trans('adresse dans vos coordonnées'), + 'coords.postcode' => trans('code postal dans vos coordonnées'), + 'coords.phone' => trans('téléphone dans vos coordonnées'), + 'coords.city' => trans('ville dans vos coordonnées'), + 'coords.email' => trans('e-mail dans vos coordonnées'), + 'shipping' => trans('votre adresse de livraison'), + 'shipping.civility' => trans('civilité dans votre adresse de livraison'), + 'shipping.firstname' => trans('prénom dans votre adresse de livraison'), + 'shipping.lastname' => trans('nom dans votre adresse de livraison'), + 'shipping.address' => trans('adresse dans votre adresse de livraison'), + 'shipping.postcode' => trans('code postal dans votre adresse de livraison'), + 'shipping.phone' => trans('téléphone dans votre adresse de livraison'), + 'shipping.city' => trans('ville dans votre adresse de livraison'), + 'cart' => trans('panier'), + 'cart.*' => trans('panier'), + 'cart.*.count' => trans('nombre d\'éléments dans le panier'), + 'cart.*.item.id' => trans('élément dans le panier'), + ]; + } + + /** + * Get custom messages for validator errors. + * + * @return array + */ + public function messages(): array + { + return [ + 'coords.postcode.postal_code' => trans( + 'Le champ :attribute accepte les codes postaux des pays suivants :countries (exemple: :examples).' + ), + 'shipping.postcode.postal_code' => trans( + 'Le champ :attribute accepte les codes postaux des pays suivants :countries (exemple: :examples).' + ), + 'coords.phone.phone' => trans( + 'Le champ :attribute est invalide, il accepte les numéros des pays suivants France.' + ), + 'shipping.phone.phone' => trans( + 'Le champ :attribute est invalide, il accepte les numéros des pays suivants France.' + ) + ]; + } +} diff --git a/app/Http/Requests/Club/Surveys/StoreSurveyAnswerRequest.php b/app/Http/Requests/Club/Surveys/StoreSurveyAnswerRequest.php index a1dd47d0..35943106 100644 --- a/app/Http/Requests/Club/Surveys/StoreSurveyAnswerRequest.php +++ b/app/Http/Requests/Club/Surveys/StoreSurveyAnswerRequest.php @@ -2,7 +2,7 @@ namespace App\Http\Requests\Club\Surveys; -use App\Enums\Club\NewsClubTypeEnum; +use App\Enums\News\NewsClubTypeEnum; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Auth; @@ -13,7 +13,7 @@ class StoreSurveyAnswerRequest extends FormRequest * * @return boolean */ - public function authorize() + public function authorize(): bool { return Auth::guard('club')->check(); } @@ -38,7 +38,7 @@ class StoreSurveyAnswerRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { return [ 'news_club_id' => 'required|integer|exists:news_clubs,id', @@ -64,7 +64,7 @@ class StoreSurveyAnswerRequest extends FormRequest * * @return array */ - public function attributes() + public function attributes(): array { return [ 'news_club_id' => trans_choice('models.classes.news_club', 1), diff --git a/app/Http/Requests/Fo/ContactFormRequest.php b/app/Http/Requests/Fo/ContactFormRequest.php index fd20145b..f3e09184 100644 --- a/app/Http/Requests/Fo/ContactFormRequest.php +++ b/app/Http/Requests/Fo/ContactFormRequest.php @@ -5,10 +5,8 @@ namespace App\Http\Requests\Fo; use App\Enums\ContactFormCivility; use App\Enums\ContactFormType; use App\Lib\Helpers\ToolboxHelper; -use Carbon\Carbon; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; -use Illuminate\Validation\Rules\Enum; class ContactFormRequest extends FormRequest { @@ -36,39 +34,37 @@ class ContactFormRequest extends FormRequest 'required', Rule::enum(ContactFormType::class)->only(ContactFormType::getContactTypes()) ], - 'civility' => [ - 'required', - Rule::enum(ContactFormCivility::class) - ], - // * max arcadia 50 chars - 'lastname' => 'required|min:2|max:50', - // * max arcadia 50 chars + 'civility' => ['required', Rule::enum(ContactFormCivility::class)], + // Max arcadia 50 chars. + 'lastname' => 'required|min:2|max:50', + // Max arcadia 50 chars. 'firstname' => 'required|min:2|max:50', - 'address' => 'required|min:4', - 'email' => 'required|email:rfc,dns,spoof', - 'postal' => 'required|postal_code:FR', - // * max arcadia 10 chars - 'town' => 'required|min:3|max:90', - 'phone' => 'required|phone:FR', + 'address' => 'required|min:4', + 'email' => 'required|email:rfc,dns,spoof', + 'postal' => 'required|postal_code:FR', + // Max arcadia 10 chars. + 'town' => 'required|min:3|max:90', + 'phone' => 'required|phone:FR', + // FIXME: uncomment when form on export page is ready. // 'onepagesubject' => 'sometimes|min:10|max:250', - 'message' => 'required|min:20|max:1900' + 'message' => 'required|min:20|max:1900' ]; if (ContactFormType::make($this->input('type')) === ContactFormType::complain) { $rules = array_merge( $rules, [ - 'productName' => 'required|min:2|max:100', - 'productCode' => 'required|digits_between:8,13|min:1|max:9999999999999', - 'buyPlace' => 'required|min:2|max:100', - 'lotNumberName' => 'required|min:2|max:50', - 'limitDateDay' => 'required|numeric|min:1|max:31', - 'limitDateMonth' => 'required|numeric|min:1|max:31', - 'limitDateYear' => 'required|numeric|min:2010|max:2050', - 'consumptionDateDay' => 'required|numeric|min:1|max:31', + 'productName' => 'required|min:2|max:100', + 'productCode' => 'required|digits_between:8,13|min:1|max:9999999999999', + 'buyPlace' => 'required|min:2|max:100', + 'lotNumberName' => 'required|min:2|max:50', + 'limitDateDay' => 'required|numeric|min:1|max:31', + 'limitDateMonth' => 'required|numeric|min:1|max:31', + 'limitDateYear' => 'required|numeric|min:2010|max:2050', + 'consumptionDateDay' => 'required|numeric|min:1|max:31', 'consumptionDateMonth' => 'required|numeric|min:1|max:31', - 'consumptionDateYear' => 'required|numeric|min:2010|max:2050', - 'joinFile' => 'nullable|mimes:jpg,jpeg,png,pdf|max:2048' + 'consumptionDateYear' => 'required|numeric|min:2010|max:2050', + 'joinFile' => 'nullable|mimes:jpg,jpeg,png,pdf|max:2048' ], ); } diff --git a/app/Jobs/Arcadia/SendHasOrderedAnItemMail.php b/app/Jobs/Arcadia/SendHasOrderedAnItemMail.php new file mode 100644 index 00000000..1024c8b1 --- /dev/null +++ b/app/Jobs/Arcadia/SendHasOrderedAnItemMail.php @@ -0,0 +1,51 @@ +itemData = $itemData; + } + + /** + * Execute the job. + * + * @return void + * @throws \RuntimeException If no config email is set. + */ + public function handle(): void + { + $emailTo = \config('arcadia.item_ordered_email_to'); + if (empty($emailTo)) { + throw new \RuntimeException('Empty arcadia.item_ordered_email_to config, could not send mail'); + } + Mail::queueOn('database', (new HasOrderedAnItem($this->itemData))->to($emailTo)); + } +} diff --git a/app/Jobs/Arcadia/SendOrderToArcadia.php b/app/Jobs/Arcadia/SendOrderToArcadia.php new file mode 100644 index 00000000..535acb47 --- /dev/null +++ b/app/Jobs/Arcadia/SendOrderToArcadia.php @@ -0,0 +1,66 @@ +order = $order; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle(): void + { + /** @var \App\Models\Order|\App\Models\DeliveryAddress */ + $deliver = $this->order->deliveryAddress()->exists() ? $this->order->deliveryAddress : $this->order; + $this->order->sellItems->each(function (SellItem $sellItem) use ($deliver) { + // @phpstan-ignore-next-line + for ($i = 0; $i < $sellItem->pivot->quantity; $i++) { + \dispatch(new SendHasOrderedAnItemMail( + new HasOrderedAnItemData( + $this->order, + $sellItem, + $deliver->firstname, + $deliver->lastname, + $deliver->address, + null, + $deliver->city, + $deliver->postcode, + $deliver->phone + ) + ))->onConnection('database')->onQueue('database'); + } //end for + }); + + Log::debug(sprintf('Order %d sent to arcadia', $this->order->id)); + } +} diff --git a/app/Jobs/FetchDigitalRecruiterOffers.php b/app/Jobs/FetchDigitalRecruiterOffers.php index 9903f216..b30a7119 100644 --- a/app/Jobs/FetchDigitalRecruiterOffers.php +++ b/app/Jobs/FetchDigitalRecruiterOffers.php @@ -37,7 +37,6 @@ class FetchDigitalRecruiterOffers config('services.digital_recruiter.url') )); } - $this->parseDigitalRecruiterOffers(\collect($response->object()->ads)); } diff --git a/app/Jobs/Mails/SendPasserelleMail.php b/app/Jobs/Mails/SendPasserelleMail.php index 319c542a..a83b9f68 100644 --- a/app/Jobs/Mails/SendPasserelleMail.php +++ b/app/Jobs/Mails/SendPasserelleMail.php @@ -10,7 +10,6 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; -use Illuminate\Support\Facades\Storage; class SendPasserelleMail implements ShouldQueue { @@ -20,19 +19,25 @@ class SendPasserelleMail implements ShouldQueue /** * To be serialized by jobs system * - * @var MailContactData + * @var \App\Models\MailContactData * */ private $data; + /** + * The path of a specific file. + * + * @var string + * */ private $file; /** * Create a new job instance. * * @param \App\Models\MailContactData $data + * @param string $file * @return void */ - public function __construct(MailContactData $data, mixed $file) + public function __construct(MailContactData $data, string $file) { $this->data = $data; $this->file = $file; @@ -43,7 +48,7 @@ class SendPasserelleMail implements ShouldQueue * * @return void */ - public function handle() + public function handle(): void { Mail::to(config('mail.contactsList.passerelle'))->send(new ContactComplainMail($this->data, $this->file)); if (file_exists($this->file)) { diff --git a/app/Jobs/Shop/SendShopMail.php b/app/Jobs/Shop/SendShopMail.php new file mode 100644 index 00000000..cb97cf6c --- /dev/null +++ b/app/Jobs/Shop/SendShopMail.php @@ -0,0 +1,54 @@ +order = $order; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle(): void + { + if (!$this->order->confirmMailSent) { + /** @var \App\Models\Customer $customerModel */ + $customerModel = $this->order->customer; + $customerModel->notify(new NewOrder($this->order)); + + $this->order->confirmMailSent = true; + $this->order->saveOrFail(); + Log::debug('Shop mail is sent', $this->order->toArray()); + } else { + Log::debug('Shop mail is already sent', $this->order->toArray()); + } + } +} diff --git a/app/Lib/Arcadia/HasOrderedAnItemData.php b/app/Lib/Arcadia/HasOrderedAnItemData.php new file mode 100644 index 00000000..63a066c3 --- /dev/null +++ b/app/Lib/Arcadia/HasOrderedAnItemData.php @@ -0,0 +1,144 @@ +properties)) { + return $this->data[$name]; + } + throw new \Error("Cannot access property {$name} it dos not exists"); + } + + /** + * Has ordered an item data. + * + * @param \App\Models\Order $order + * @param \App\Models\SellItem $sellItem + * @param string $firstname + * @param string $lastname + * @param string $address + * @param string|null $address2 + * @param string $city + * @param string $postcode + * @param string|null $phone + * @return void + */ + public function __construct( + Order $order, + SellItem $sellItem, + string $firstname, + string $lastname, + string $address, + ?string $address2, + string $city, + string $postcode, + ?string $phone + ) { + $this->data = [ + 'operation' => \intval(env('BOUTIQUE_ARCADIA_OP_CODE', 16)), + 'support_code' => $sellItem->arcadia_id, + 'label_type_support' => $sellItem->name, + 'order_date' => $order->created_at, + 'order_num' => $order->id, + 'state_order' => ArcadiaOrderType::check->value(), + // FIXME: uncomment when mercanet cb is ready. + // 'state_order' => $order->payementType->equals(OrderType::cb) ? + // ArcadiaOrderType::cb->value() : ArcadiaOrderType::check->value(), + 'package_unit_weight' => $sellItem->arcadia_weight, + 'email' => $order->email, + 'firstname' => $firstname, + 'lastname' => $lastname, + 'address' => $address, + 'address2' => $address2, + 'city' => $city, + 'postcode' => $postcode, + 'phone' => $phone + ]; + } + + /** + * Convert the object to its JSON representation. + * + * @param integer $options + * @return string + * @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing + */ + public function toJson($options = 0): string + { + // phpcs:enable + return \json_encode($this->toArray(), $options); + } + + /** + * Get the instance as an array. + * + * @return array + */ + public function toArray(): array + { + return $this->data; + } +} diff --git a/app/Lib/Definitions/MenuNode.php b/app/Lib/Definitions/MenuNode.php index 7c21ea55..6d7e85db 100644 --- a/app/Lib/Definitions/MenuNode.php +++ b/app/Lib/Definitions/MenuNode.php @@ -15,13 +15,13 @@ use App\Lib\Link; class MenuNode extends \stdClass { /** - * Get property + * Get property. * * @param string $name * @return mixed * @throws \TypeError If prop is undefined. */ - public function __get(string $name) + public function __get(string $name): mixed { if (in_array($name, ['type', 'title', 'link', 'published'])) { return $this->{$name}; @@ -62,7 +62,7 @@ class MenuNode extends \stdClass * @return void * @throws \RuntimeException If $type is wrong. */ - public static function assertTypeIsCorrect(int $type) + public static function assertTypeIsCorrect(int $type): void { try { $type = MenuNodeType::make($type); diff --git a/app/Lib/Helpers/CollectionHelper.php b/app/Lib/Helpers/CollectionHelper.php index b9e9ecda..bbb51aba 100644 --- a/app/Lib/Helpers/CollectionHelper.php +++ b/app/Lib/Helpers/CollectionHelper.php @@ -10,13 +10,13 @@ use Illuminate\Support\Collection; class CollectionHelper { /** - * Paginate a collection + * Paginate a collection. * * @param \Illuminate\Support\Collection $results * @param integer $pageSize * @return \Illuminate\Pagination\LengthAwarePaginator */ - public static function paginate(Collection $results, int $pageSize): LengthAwarePaginator + public static function paginate(Collection $results, int $pageSize): \Illuminate\Pagination\LengthAwarePaginator { $page = Paginator::resolveCurrentPage('page'); @@ -24,15 +24,18 @@ class CollectionHelper } /** - * Paginate a collection + * Paginate a collection. * * @param \Illuminate\Support\Collection $results * @param integer $pageSize * @param integer $page * @return \Illuminate\Pagination\LengthAwarePaginator */ - public static function paginateManually(Collection $results, int $pageSize, int $page = 1): LengthAwarePaginator - { + public static function paginateManually( + Collection $results, + int $pageSize, + int $page = 1 + ): \Illuminate\Pagination\LengthAwarePaginator { $total = $results->count(); return self::paginator($results->forPage($page, $pageSize), $total, $pageSize, $page, [ @@ -51,14 +54,19 @@ class CollectionHelper * @param array $options * @return \Illuminate\Pagination\LengthAwarePaginator */ - protected static function paginator(Collection $items, int $total, int $perPage, int $currentPage, array $options) - { + protected static function paginator( + Collection $items, + int $total, + int $perPage, + int $currentPage, + array $options + ): \Illuminate\Pagination\LengthAwarePaginator { return Container::getInstance()->makeWith(LengthAwarePaginator::class, [ - 'items' => $items, - 'total' => $total, - 'perPage' => $perPage, + 'items' => $items, + 'total' => $total, + 'perPage' => $perPage, 'currentPage' => $currentPage, - 'options' => $options + 'options' => $options ]); } } diff --git a/app/Lib/Helpers/ExceptionsHelper.php b/app/Lib/Helpers/ExceptionsHelper.php index 30deb548..3c46130c 100644 --- a/app/Lib/Helpers/ExceptionsHelper.php +++ b/app/Lib/Helpers/ExceptionsHelper.php @@ -5,7 +5,7 @@ namespace App\Lib\Helpers; class ExceptionsHelper { /** - * Handle sentry event + * Handle sentry event. * * @param \Sentry\Event $event * @return null|\Sentry\Event @@ -26,7 +26,7 @@ class ExceptionsHelper } /** - * Anonimize Sentry Exception bag + * Anonimize Sentry Exception bag. * * @param \Sentry\ExceptionDataBag $exceptionDataBag * @return \Sentry\ExceptionDataBag @@ -42,7 +42,7 @@ class ExceptionsHelper } /** - * Anonymise sentry stacktrace + * Anonymise sentry stacktrace. * * @param \Sentry\Stacktrace $stackTrace * @return \Sentry\Stacktrace @@ -70,7 +70,7 @@ class ExceptionsHelper } /** - * Get anonymized sentry params + * Get anonymized sentry params. * * @param mixed[] $params * @return string[] @@ -103,9 +103,8 @@ class ExceptionsHelper return $params; } - /** - * Anonymize at least Sql queries + * Anonymize at least Sql queries. * * @param string $message * @return string @@ -120,7 +119,7 @@ class ExceptionsHelper } /** - * Anonymize stack trace + * Anonymize stack trace. * * @param \Throwable $e * @return string @@ -159,7 +158,7 @@ class ExceptionsHelper } /** - * Handle Args + * Handle Args. * * @param array $stack * @return string diff --git a/app/Lib/Helpers/FileStorageHelper.php b/app/Lib/Helpers/FileStorageHelper.php index 6a520555..20e7c89b 100644 --- a/app/Lib/Helpers/FileStorageHelper.php +++ b/app/Lib/Helpers/FileStorageHelper.php @@ -25,8 +25,6 @@ use Illuminate\Support\Str; * The final path will be storage/modelfiles/posts/10/04/19/5f/black-cat-under-red-car-286071690833400.png * * ! No limits of file per folder will be handled. - * - * @author Munsch Jérémy */ class FileStorageHelper { diff --git a/app/Lib/Helpers/ToolboxHelper.php b/app/Lib/Helpers/ToolboxHelper.php index eb2c9e7e..562e4517 100644 --- a/app/Lib/Helpers/ToolboxHelper.php +++ b/app/Lib/Helpers/ToolboxHelper.php @@ -22,7 +22,7 @@ use Symfony\Component\Finder\SplFileInfo; class ToolboxHelper { /** - * Censor string + * Censor string. * * @param string $string * @return string @@ -38,8 +38,9 @@ class ToolboxHelper } return $censor->censorString($string, true)['clean']; } + /** - * Run a terminal command and gives the output + * Run a terminal command and gives the output. * * @param array|string $command The command to execute on terminal. * @param boolean $throw Wether to throw en esception on failure or not. @@ -53,7 +54,7 @@ class ToolboxHelper * @throws \LogicException * @throws \RuntimeException If the command fails. */ - public static function runConsoleCommand(array|string $command, bool $throw = true): string | false + public static function runConsoleCommand(array|string $command, bool $throw = true): string|false { // phpcs:enable $process = \Illuminate\Support\Facades\Process::command($command)->run(); @@ -69,6 +70,8 @@ class ToolboxHelper } /** + * Return array merge recursive distinct. + * * @param array $array1 * @param array $array2 * @@ -89,13 +92,13 @@ class ToolboxHelper } /** - * Validate recaptcha + * Validate recaptcha. * * @param \Illuminate\Http\Request $request * @param boolean $isV3 * @return void */ - public static function validateRecaptcha(Request $request, bool $isV3 = true) + public static function validateRecaptcha(Request $request, bool $isV3 = true): void { Validator::validate($request->all(), [ 'g-recaptcha-response' => [ @@ -147,7 +150,7 @@ class ToolboxHelper * Only embed allowed are for MP4 self hosted videos right now * * https is force by ckeditor MediaElements which natively supports - * only cloud hosted medias (youtube, spotify etc..) + * only cloud hosted medias (youtube, spotify etc..). * * @param string $input * @return string @@ -159,7 +162,7 @@ class ToolboxHelper } /** - * Assert That no Back Office url is present on txt + * Assert That no Back Office url is present on txt. * * @param string $field * @param string $text @@ -202,7 +205,7 @@ class ToolboxHelper } /** - * Assert That only external url is present on txt + * Assert That only external url is present on txt. * * @param string $field * @param string $text @@ -245,7 +248,7 @@ class ToolboxHelper } /** - * Mutltibyte string replace + * Mutltibyte string replace. * * @param array|string $search * @param array|string $replace @@ -289,7 +292,7 @@ class ToolboxHelper } /** - * Asserts that the field is unique + * Asserts that the field is unique. * * @param string $table * @param string $field @@ -305,7 +308,7 @@ class ToolboxHelper string|int|float|bool|null|array $value, ?int $id = null, ?callable $query = null - ) { + ): void { if (!$query) { Validator::make([$field => $value], [ self::mbReplace('.', '\.', $field) => Rule::unique($table, $field)->ignore($id), @@ -326,7 +329,7 @@ class ToolboxHelper } /** - * Get original polyfill with autotranslate behavior + * Get original polyfill with autotranslate behavior. * * @param \Illuminate\Database\Eloquent\Model $model * @param string $attribute @@ -352,7 +355,7 @@ class ToolboxHelper } /** - * Dynamic model query + * Dynamic model query. * * @param string $defaultLocale * @param \Illuminate\Contracts\Database\Eloquent\Builder $q @@ -365,7 +368,7 @@ class ToolboxHelper Builder $q, string $column, string $value - ): Builder { + ): \Illuminate\Contracts\Database\Eloquent\Builder { $currentLocale = app()->currentLocale(); $locales = collect(config('app.locales')) ->mapWithKeys(fn (string $locale) => [$locale => $locale]); @@ -393,7 +396,7 @@ class ToolboxHelper } /** - * Generates an array from a dot separated string + * Generates an array from a dot separated string. * * @param string $path * @param mixed $value @@ -412,7 +415,7 @@ class ToolboxHelper } /** - * Convert deeply an object to array + * Convert deeply an object to array. * * @param mixed $input * @return array @@ -434,7 +437,7 @@ class ToolboxHelper } /** - * Convert deeply an array to object + * Convert deeply an array to object. * * @param mixed $input * @return object @@ -456,7 +459,7 @@ class ToolboxHelper } /** - * Validate picture + * Validate picture. * * @param string $attribute * @param string|null $value @@ -513,7 +516,7 @@ class ToolboxHelper } /** - * Get the file full path + * Get the file full path. * * @param string $value * @param boolean $useStorage @@ -527,19 +530,19 @@ class ToolboxHelper } /** - * Get the file mimetype + * Get the file mimetype. * * @param string $value * @param boolean $useStorage * @return string|false */ - private static function getUploadedFileMimeType(string $value, bool $useStorage) + private static function getUploadedFileMimeType(string $value, bool $useStorage): string|false { return !$useStorage ? File::mimeType($value) : Storage::mimeType($value); } /** - * Fetch class list from a specific namespace + * Fetch class list from a specific namespace. * * @param string $namespace * @return array An array of classe namespaces @@ -553,7 +556,7 @@ class ToolboxHelper } /** - * Get all classes from a specific namespace that implement a specific interface + * Get all classes from a specific namespace that implement a specific interface. * * @param string $namespace * @param string $interface diff --git a/app/Lib/Link.php b/app/Lib/Link.php index a8526313..6f0d255e 100644 --- a/app/Lib/Link.php +++ b/app/Lib/Link.php @@ -11,10 +11,10 @@ use Stringable; * * @package App\Lib */ -class Link implements Jsonable, Arrayable, \JsonSerializable, Stringable +class Link implements Jsonable, Arrayable, Stringable { /** - * Link + * Link. * * @param null|string $url A valid url. * @param null|string $routeName A valid route name. @@ -77,9 +77,9 @@ class Link implements Jsonable, Arrayable, \JsonSerializable, Stringable /** * Get the instance as an array. * - * @return array<'url'|'routeName',string|null> + * @return array<'url'|'routeName', string|null> */ - public function toArray() + public function toArray(): array { return ['url' => $this->url, 'routeName' => $this->routeName]; } @@ -91,26 +91,14 @@ class Link implements Jsonable, Arrayable, \JsonSerializable, Stringable * @return string * @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing */ - public function toJson($options = 0) + public function toJson($options = 0): string { // phpcs:enable - return \json_encode($this->jsonSerialize(), $options); - } - - /** - * Convert the object into something JSON serializable. - * - * @return array - */ - #[\ReturnTypeWillChange] - public function jsonSerialize() - { - return $this->toArray(); + return \json_encode($this->toArray(), $options); } /** * @return string - * * @throws \RuntimeException If the link cannot be converted to string. */ public function __toString() diff --git a/app/Lib/Mercanet/Mercanet.php b/app/Lib/Mercanet/Mercanet.php new file mode 100644 index 00000000..c439b7c2 --- /dev/null +++ b/app/Lib/Mercanet/Mercanet.php @@ -0,0 +1,1114 @@ + */ + private $brandsmap = [ + 'ACCEPTGIRO' => 'CREDIT_TRANSFER', + 'AMEX' => 'CARD', + 'BCMC' => 'CARD', + 'BUYSTER' => 'CARD', + 'BANK CARD' => 'CARD', + 'CB' => 'CARD', + 'IDEAL' => 'CREDIT_TRANSFER', + 'INCASSO' => 'DIRECT_DEBIT', + 'MAESTRO' => 'CARD', + 'MASTERCARD' => 'CARD', + 'MASTERPASS' => 'CARD', + 'MINITIX' => 'OTHER', + 'NETBANKING' => 'CREDIT_TRANSFER', + 'PAYPAL' => 'CARD', + 'PAYLIB' => 'CARD', + 'REFUND' => 'OTHER', + 'SDD' => 'DIRECT_DEBIT', + 'SOFORT' => 'CREDIT_TRANSFER', + 'VISA' => 'CARD', + 'VPAY' => 'CARD', + 'VISA ELECTRON' => 'CARD', + 'CBCONLINE' => 'CREDIT_TRANSFER', + 'KBCONLINE' => 'CREDIT_TRANSFER' + ]; + + private $secretKey; + + private $env; + + /** @var string $returnUrl */ + private $returnUrl; + + /** @var boolean|string */ + private $automaticUrl = false; + + private $pspURL = self::DEVELOPMENT; + + /** @var string */ + private $responseData; + + /** @var array */ + private $parameters = []; + + /** @var array */ + private $pspFields = [ + 'amount', + 'cardExpiryDate', + 'cardNumber', + 'cardCSCValue', + 'currencyCode', + 'merchantId', + 'interfaceVersion', + 'sealAlgorithm', + 'transactionReference', + 'keyVersion', + 'paymentMeanBrand', + 'customerLanguage', + 'billingAddress.city', + 'billingAddress.company', + 'billingAddress.country', + 'billingAddress', + 'billingAddress.postBox', + 'billingAddress.state', + 'billingAddress.street', + 'billingAddress.streetNumber', + 'billingAddress.zipCode', + 'billingContact.email', + 'billingContact.firstname', + 'billingContact.gender', + 'billingContact.lastname', + 'billingContact.mobile', + 'billingContact.phone', + 'customerAddress', + 'customerAddress.city', + 'customerAddress.company', + 'customerAddress.country', + 'customerAddress.postBox', + 'customerAddress.state', + 'customerAddress.street', + 'customerAddress.streetNumber', + 'customerAddress.zipCode', + 'customerEmail', + 'customerContact', + 'customerContact.email', + 'customerContact.firstname', + 'customerContact.gender', + 'customerContact.lastname', + 'customerContact.mobile', + 'customerContact.phone', + 'customerContact.title', + 'expirationDate', + 'automaticResponseUrl', + 'templateName', + 'paymentMeanBrandList', + 'instalmentData.number', + 'instalmentData.datesList', + 'instalmentData.transactionReferencesList', + 'instalmentData.amountsList', + 'paymentPattern', + 'captureDay', + 'captureMode', + 'merchantTransactionDateTime', + 'fraudData.bypass3DS', + 'seal', + 'orderChannel', + 'orderId', + 'returnContext', + 'transactionOrigin', + 'merchantWalletId', + 'paymentMeanId' + ]; + + /** @var array */ + private $requiredFields = [ + 'amount', + 'currencyCode', + 'interfaceVersion', + 'keyVersion', + 'merchantId', + 'normalReturnUrl', + 'orderChannel', + 'transactionReference' + ]; + + /** @var array */ + public $allowedlanguages = [ + 'nl', + 'fr', + 'de', + 'it', + 'es', + 'cy', + 'en' + ]; + + /** @var array */ + private static $currencies = [ + 'EUR' => '978', + 'USD' => '840', + 'CHF' => '756', + 'GBP' => '826', + 'CAD' => '124', + 'JPY' => '392', + 'MXP' => '484', + 'TRY' => '949', + 'AUD' => '036', + 'NZD' => '554', + 'NOK' => '578', + 'BRC' => '986', + 'ARP' => '032', + 'KHR' => '116', + 'TWD' => '901', + 'SEK' => '752', + 'DKK' => '208', + 'KRW' => '410', + 'SGD' => '702', + 'XPF' => '953', + 'XOF' => '952' + ]; + + public function __construct($secret, $MerchantId, $env, $returnUrl, $automaticUrl = false) + { + $this->secretKey = $secret; + $this->parameters['merchantId'] = $MerchantId; + $this->env = $env; + $this->returnUrl = $returnUrl; + if ($automaticUrl !== false) { + $this->automaticUrl = $automaticUrl; + } + + switch ($this->env) { + case 'DEVELOPMENT': + $this->setUrl(Mercanet::DEVELOPMENT); + break; + case 'PRODUCTION': + $this->setUrl(Mercanet::PRODUCTION); + break; + } + } + + public static function convertCurrencyToCurrencyCode($currency) + { + if (!in_array($currency, array_keys(self::$currencies))) { + throw new InvalidArgumentException("Unknown currencyCode $currency."); + } + return self::$currencies[$currency]; + } + + public static function convertCurrencyCodeToCurrency($code) + { + if (!in_array($code, array_values(self::$currencies))) { + throw new InvalidArgumentException("Unknown Code $code."); + } + return array_search($code, self::$currencies); + } + + /** + * Get currencies. + * + * @return array + */ + public static function getCurrencies(): array + { + return self::$currencies; + } + + /** + * Sha compose. + * + * @param array $parameters + * @return string + */ + public function shaCompose(array $parameters): string + { + $shaString = ''; + foreach ($parameters as $key => $value) { + if ($key != 'keyVersion' && $key != 'sealAlgorithm') { + $shaString .= (is_array($value)) ? implode('', $value) : $value; + } + } + return $shaString; + } + + /** + * Get sha sign. + * + * @return string + */ + public function getShaSign(): string + { + $this->validate(); + return hash_hmac('sha256', utf8_encode($this->shaCompose($this->toArray())), $this->secretKey); + } + + /** + * Get url. + * + * @return string + */ + public function getUrl(): string + { + return $this->pspURL; + } + + /** + * Set url. + * + * @param string $pspUrl + * @return void + */ + public function setUrl(string $pspUrl): void + { + $this->validateUri($pspUrl); + $this->pspURL = $pspUrl; + } + + /** + * Set normal return url. + * + * @param string $url + * @return void + */ + public function setNormalReturnUrl(string $url): void + { + $this->validateUri($url); + $this->parameters['normalReturnUrl'] = $url; + } + + /** + * Set automatic response url. + * + * @param string $url + * @return void + */ + public function setAutomaticResponseUrl(string $url): void + { + $this->validateUri($url); + $this->parameters['automaticResponseUrl'] = $url; + } + + /** + * Set automatic response url. + * + * @param string $url + * @return void + */ + public function setTransactionReference(string $transactionReference): void + { + if (preg_match('/[^a-zA-Z0-9_-]/', $transactionReference)) { + throw new \InvalidArgumentException("TransactionReference cannot contain special characters"); + } + $this->parameters['transactionReference'] = $transactionReference; + } + + /** + * Set amount in cents, eg EUR 12.34 is written as 1234. + * + * @param mixed $amount + * @return void + */ + public function setAmount(mixed $amount): void + { + if (!is_int($amount)) { + throw new InvalidArgumentException("Integer expected. Amount is always in cents"); + } + if ($amount <= 0) { + throw new InvalidArgumentException("Amount must be a positive number"); + } + $this->parameters['amount'] = "$amount"; + } + + /** + * Set currency. + * + * @param string $currency + * @return void + */ + public function setCurrency(string $currency): void + { + if (!array_key_exists(strtoupper($currency), self::getCurrencies())) { + throw new InvalidArgumentException("Unknown currency"); + } + $this->parameters['currencyCode'] = self::convertCurrencyToCurrencyCode($currency); + } + + /** + * Set language. + * + * @param string $language + * @return void + */ + public function setLanguage(string $language): void + { + if (!in_array($language, $this->allowedlanguages)) { + throw new InvalidArgumentException("Invalid language locale"); + } + $this->parameters['customerLanguage'] = $language; + } + + /** + * Set customer email. + * + * @param string $email + * @return void + */ + public function setCustomerEmail(string $email): void + { + $this->parameters['customerEmail'] = $email; + } + + /** + * Set payment brands. + * + * @param array $brands + * @return void + */ + public function setPaymentBrands(array $brands): void + { + $this->parameters['paymentMeanBrandList'] = ''; + foreach ($brands as $brand) { + if (!array_key_exists(strtoupper($brand), $this->brandsmap)) { + throw new InvalidArgumentException("Unknown Brand [$brand]."); + } + } + $this->parameters['paymentMeanBrandList'] = $brands; + } + + /** + * Set billing contacte email. + * + * @param string $email + * @return void + */ + public function setBillingContactEmail(string $email): void + { + if (strlen($email) > 50) { + throw new InvalidArgumentException("Email is too long"); + } + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + throw new InvalidArgumentException("Email is invalid"); + } + $this->parameters['billingContact.email'] = $email; + } + + /** + * Set billing address street. + * + * @param string $street + * @return void + */ + public function setBillingAddressStreet(string $street): void + { + if (strlen($street) > 35) { + throw new InvalidArgumentException("street is too long"); + } + $this->parameters['billingAddress.street'] = Normalizer::normalize($street); + } + + /** + * Set billing address street number. + * + * @param string $nr + * @return void + */ + public function setBillingAddressStreetNumber(string $nr): void + { + if (strlen($nr) > 10) { + throw new InvalidArgumentException("streetNumber is too long"); + } + $this->parameters['billingAddress.streetNumber'] = Normalizer::normalize($nr); + } + + /** + * Set billing address zip code. + * + * @param string $zipCode + * @return void + */ + public function setBillingAddressZipCode(string $zipCode): void + { + if (strlen($zipCode) > 10) { + throw new InvalidArgumentException("zipCode is too long"); + } + $this->parameters['billingAddress.zipCode'] = Normalizer::normalize($zipCode); + } + + /** + * Set billing address city. + * + * @param string $city + * @return void + */ + public function setBillingAddressCity(string $city): void + { + if (strlen($city) > 25) { + throw new InvalidArgumentException("city is too long"); + } + $this->parameters['billingAddress.city'] = Normalizer::normalize($city); + } + + /** + * Set billing contact phone. + * + * @param string $phone + * @return void + */ + public function setBillingContactPhone(string $phone): void + { + if (strlen($phone) > 30) { + throw new InvalidArgumentException("phone is too long"); + } + $this->parameters['billingContact.phone'] = $phone; + } + + /** + * Set billing contact firstname. + * + * @param string $firstname + * @return void + */ + public function setBillingContactFirstname(string $firstname): void + { + // replace quotes + $this->parameters['billingContact.firstname'] = str_replace( + array("'", '"'), + '', + Normalizer::normalize($firstname) + ); + } + + /** + * Set billing contact lastname. + * + * @param string $lastname + * @return void + */ + public function setBillingContactLastname(string $lastname): void + { + // replace quotes + $this->parameters['billingContact.lastname'] = str_replace( + array("'", '"'), + '', + Normalizer::normalize($lastname) + ); + } + + /** + * Set capture day. + * + * @param string $number + * @return void + */ + public function setCaptureDay(string $number): void + { + if (strlen($number) > 2) { + throw new InvalidArgumentException("captureDay is too long"); + } + $this->parameters['captureDay'] = $number; + } + + /** + * Set capture mode. + * + * @param string $value + * @return void + */ + public function setCaptureMode(string $value): void + { + if (strlen($value) > 20) { + throw new InvalidArgumentException("captureMode is too long"); + } + $this->parameters['captureMode'] = $value; + } + + /** + * Set merchant transaction date time. + * + * @param string $value + * @return void + */ + public function setMerchantTransactionDateTime(string $value): void + { + if (strlen($value) > 25) { + throw new InvalidArgumentException("merchantTransactionDateTime is too long"); + } + $this->parameters['merchantTransactionDateTime'] = $value; + } + + /** + * Set interface version. + * + * @param string $value + * @return void + */ + public function setInterfaceVersion(string $value): void + { + $this->parameters['interfaceVersion'] = $value; + } + + /** + * setkeyVersion method + * + * @param string $version + * @return void + */ + public function setKeyVersion(string $version): void + { + $this->parameters['keyVersion'] = $version; + } + + /** + * Set seal algorithm. + * + * @param string $value + * @return void + */ + public function setSealAlgorithm(string $value): void + { + $this->parameters['sealAlgorithm'] = $value; + } + + /** + * Set seal algorithm. + * + * @param string $value + * @return void + */ + public function setOrderChannel(string $value): void + { + if (strlen($value) > 20) { + throw new InvalidArgumentException("orderChannel is too long"); + } + $this->parameters['orderChannel'] = $value; + } + + /** + * Set order id. + * + * @param string $value + * @return void + */ + public function setOrderId(string $value): void + { + if (strlen($value) > 32) { + throw new InvalidArgumentException("orderId is too long"); + } + $this->parameters['orderId'] = $value; + } + + /** + * Set return context. + * + * @param string $value + * @return void + */ + public function setReturnContext(string $value): void + { + if (strlen($value) > 255) { + throw new InvalidArgumentException("returnContext is too long"); + } + $this->parameters['returnContext'] = $value; + } + + /** + * Set transaction origin. + * + * @param string $value + * @return void + */ + public function setTransactionOrigin(string $value): void + { + if (strlen($value) > 20) { + throw new InvalidArgumentException("transactionOrigin is too long"); + } + $this->parameters['transactionOrigin'] = $value; + } + + /** + * Set card number. + * + * @param string $value + * @return void + */ + public function setCardNumber(string $number): void + { + if (strlen($number) > 19) { + throw new InvalidArgumentException("cardNumber is too long"); + } + if (strlen($number) < 12) { + throw new InvalidArgumentException("cardNumber is too short"); + } + $this->parameters['cardNumber'] = $number; + } + + /** + * Set card expiry date. + * + * @param string $value + * @return void + */ + public function setCardExpiryDate(string $date): void + { + if (strlen($date) != 6) { + throw new InvalidArgumentException("cardExpiryDate value is invalid"); + } + $this->parameters['cardExpiryDate'] = $date; + } + + /** + * Set card csc value. + * + * @param string $value + * @return void + */ + public function setCardCscValue(string $value): void + { + if (strlen($value) > 4) { + throw new InvalidArgumentException("cardCSCValue value is invalid"); + } + $this->parameters['cardCSCValue'] = $value; + } + + /** + * Set fraud data bypass3ds. + * + * @param string $value + * @return void + */ + public function setFraudDataBypass3ds(string $value): void + { + if (strlen($value) > 128) { + throw new InvalidArgumentException("fraudData.bypass3DS is too long"); + } + $this->parameters['fraudData.bypass3DS'] = $value; + } + + /** + * Set merchant wallet id. + * + * @param string $value + * @return void + */ + public function setMerchantWalletId(string $wallet): void + { + if (strlen($wallet) > 21) { + throw new InvalidArgumentException("merchantWalletId is too long"); + } + $this->parameters['merchantWalletId'] = $wallet; + } + + /** + * Set payment mean id. + * + * @param string $value + * @return void + */ + public function setPaymentMeanId(string $value): void + { + if (strlen($value) > 6) { + throw new InvalidArgumentException("paymentMeanId is too long"); + } + $this->parameters['paymentMeanId'] = $value; + } + + /** + * Set instalment data number. + * + * @param string $value + * @return void + */ + public function setInstalmentDataNumber(string $number): void + { + if (strlen($number) > 2) { + throw new InvalidArgumentException("instalmentData.number is too long"); + } + if (($number < 2) || ($number > 50)) { + throw new InvalidArgumentException( + "instalmentData.number invalid value : value must be set between 2 and 50" + ); + } + $this->parameters['instalmentData.number'] = $number; + } + + /** + * Set instalment dates list. + * + * @param string $value + * @return void + */ + public function setInstalmentDatesList(string $datesList): void + { + $this->parameters['instalmentData.datesList'] = $datesList; + } + + /** + * Set instalment data transaction references list. + * + * @param string $value + * @return void + */ + public function setInstalmentDataTransactionReferencesList(string $transactionReferencesList): void + { + $this->parameters['instalmentData.transactionReferencesList'] = $transactionReferencesList; + } + + /** + * Set instalment data amounts list. + * + * @param string $value + * @return void + */ + public function setInstalmentDataAmountsList(string $amountsList): void + { + $this->parameters['instalmentData.amountsList'] = $amountsList; + } + + /** + * Set payment pattern. + * + * @param string $value + * @return void + */ + public function setPaymentPattern(string $paymentPattern): void + { + $this->parameters['paymentPattern'] = $paymentPattern; + } + + public function __call(string $method, array $args) + { + if (substr($method, 0, 3) == 'set') { + $field = lcfirst(substr($method, 3)); + if (in_array($field, $this->pspFields)) { + $this->parameters[$field] = $args[0]; + return; + } + } + + if (substr($method, 0, 3) == 'get') { + $field = lcfirst(substr($method, 3)); + if (array_key_exists($field, $this->parameters)) { + return $this->parameters[$field]; + } + } + + throw new BadMethodCallException("Unknown method $method"); + } + + /** + * Apply sort on parameters. + * + * @return array + */ + public function toArray(): array + { + ksort($this->parameters); + return $this->parameters; + } + + /** + * To parameter string. + * + * @return string|false + */ + public function toParameterString(): string|false + { + ksort($this->parameters); + $seal = $this->getShaSign(); + $tmp = array_merge($this->parameters, ['seal' => $seal]); + return json_encode($tmp, JSON_UNESCAPED_SLASHES); + } + + public function validate() + { + foreach ($this->requiredFields as $field) { + if (empty($this->parameters[$field])) { + throw new \RuntimeException($field . " can not be empty"); + } + } + } + + /** + * Validate uri. + * + * @param string $uri + * @return void + */ + protected function validateUri(string $uri): void + { + if (!filter_var($uri, FILTER_VALIDATE_URL)) { + throw new InvalidArgumentException("Uri is not valid"); + } + if (strlen($uri) > 200) { + throw new InvalidArgumentException("Uri is too long"); + } + } + + // Traitement des reponses de Mercanet + // ----------------------------------- + + /** @var string */ + public const SHASIGN_FIELD = "SEAL"; + + /** @var string */ + public const DATA_FIELD = "DATA"; + + /** @var string */ + private $shaSign; + + private $dataString; + + private $responseRequest; + + private $parameterArray; + + /** + * Set response. + * + * @param array $httpRequest + * @return void + */ + public function setResponse(array $httpRequest): void + { + // Use lowercase internally. + $httpRequest = array_change_key_case($httpRequest, CASE_UPPER); + + // Set sha sign. + $this->shaSign = $this->extractShaSign($httpRequest); + + // Filter request for Sips parameters. + $this->parameters = $this->filterRequestParameters($httpRequest); + } + + /** + * Filter http request parameters. + * + * @param array $requestParameters + * @return array + */ + private function filterRequestParameters(array $httpRequest): array + { + // Filter request for Sips parameters. + if (!array_key_exists(self::DATA_FIELD, $httpRequest) || $httpRequest[self::DATA_FIELD] == '') { + throw new InvalidArgumentException('Data parameter not present in parameters.'); + } + $this->responseData = $this->dataString = $httpRequest[self::DATA_FIELD]; + return \json_decode($this->dataString, true); + } + + /** + * Get seal. + * + * @return string + */ + public function getSeal(): string + { + return $this->shaSign; + } + + private function extractShaSign(array $parameters) + { + if (!array_key_exists(self::SHASIGN_FIELD, $parameters) || $parameters[self::SHASIGN_FIELD] == '') { + throw new InvalidArgumentException('SHASIGN parameter not present in parameters.'); + } + + return $parameters[self::SHASIGN_FIELD]; + } + + /** + * Concat all values from parameters into on sigle string. + * + * @param array $params + * @param string $values + * @return string + */ + public function getValuesFromParameters(array $params = [], string $values = ""): string + { + foreach ($params as $value) { + $values .= !\is_array($value) ? \trim($value) : $this->getValuesFromParameters($value); + } + return $values; + } + + /** + * Checks if the response is valid. + * + * @return boolean + */ + public function isValid(): bool + { + Log::debug("Mercanet Response Seal : " . $this->shaSign); + Log::debug("Mercanet Response : " . $this->responseData); + $resultat = false; + $compute = hash_hmac('sha256', $this->responseData, $this->secretKey); + if (hash_equals($this->shaSign, $compute)) { + if ( + in_array($this->parameters['responseCode'], [ + '00', // Paiement accepté + '02', // Demande d’autorisation par téléphone à la banque à cause d’un dépassement du plafond d’autorisation sur la carte, si vous êtes autorisé à forcer les transactions + '03', // Contrat commerçant invalide + '05', // Autorisation refusée Refus 3D Secure, Refus bancaire acquéreur, Repli VADS, Fraude + '11', // Utilisé dans le cas d'un contrôle différé. Le PAN est en opposition + '12', // Transaction invalide, vérifier les paramètres transférés dans la requête + '14', // Coordonnées du moyen de paiement invalides (ex: n° de carte ou cryptogramme visuel de la carte) ou vérification AVS échouée + '17', // Annulation de l’acheteur + '30', // Erreur de format + '34', // Suspicion de fraude (seal erroné), Refus fraude acquéreur + '54', // Date de validité du moyen de paiement dépassée + '60', // Transaction en attente + '75', // Nombre de tentatives de saisie des coordonnées du moyen de paiement sous Sips Paypage dépassé + '90', // Service temporairement indisponible + '94', // Transaction dupliquée : le transactionReference de la transaction est déjà utilisé + '97', // Délai expiré, transaction refusée + '99' // Problème temporaire du serveur de paiement. + ]) + ) { + $resultat = true; + } + } //end if + return $resultat; + } + + /** + * Retrieves a response parameter. + * + * @param string $param + * @throws \InvalidArgumentException + */ + public function getParam($key) + { + return $this->parameterArray[$key]; + } + + /** + * Get response request. + * + * @return \App\Lib\Mercanet\Mercanet + */ + public function getResponseRequest(): \App\Lib\Mercanet\Mercanet + { + return $this->responseRequest; + } + + /** + * Order payment. + * + * @param integer $amount + * @param string $ref + * @return object + */ + public function quickPayment(int $amount, string $ref): object + { + // Seal. + $this->setSealAlgorithm("HMAC-SHA-256"); + // Required fields. + $this->setInterfaceVersion(Mercanet::INTERFACE_VERSION); + $this->setKeyVersion(Mercanet::VERSION); + $this->setCurrency(Mercanet::CURRENCY); + $this->setOrderChannel("INTERNET"); + // Variables. + $this->setAmount($amount); + $this->setTransactionReference($ref); + $this->setNormalReturnUrl($this->returnUrl); + if ($this->automaticUrl !== false) { + $this->setAutomaticResponseUrl($this->automaticUrl); + } + // Optional fields. + $this->setPaymentBrands(Mercanet::PAYMENT_BRANDS); + $this->setCaptureMode('IMMEDIATE'); + // Validate. + $this->validate(); + + return $this->executeRequest(); + } + + /** + * Execute request. + * + * @return object + */ + public function executeRequest(): object + { + Log::debug("Mercanet Request : " . $this->toParameterString()); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $this->getUrl()); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $this->toParameterString()); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Accept:application/json')); + curl_setopt($ch, CURLOPT_PORT, 443); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $result = curl_exec($ch); + $info = curl_getinfo($ch); + + if (!$result) { + report(new \RuntimeException( + "MERCANET [" . strval($this->parameters['transactionReference']) . "] : curl error: " . curl_error($ch) + )); + Log::critical( + "MERCANET [" . strval($this->parameters['transactionReference']) . "] : curl error: " . curl_error($ch) + ); + curl_close($ch); + return (object)['status' => 'KO', 'code' => 500]; + } + + if ($info['http_code'] != 200) { + Log::critical(sprintf( + "MERCANET [%s] : service error: %s - Return : %s", + $this->parameters['transactionReference'], + $info['http_code'], + $result + )); + curl_close($ch); + return (object)['status' => 'KO', 'code' => 500]; + } + curl_close($ch); + + if (strlen($result) == 0) { + Log::critical(sprintf( + "MERCANET [%s] : service did not sent back data", + $this->parameters['transactionReference'] + )); + return (object)['status' => 'KO', 'code' => 500]; + } + $result_array = json_decode($result); + if (isset($result_array->redirectionStatusCode)) { + if ($result_array->redirectionStatusCode === '00') { + return (object)[ + 'status' => 'OK', + 'code' => $result_array->redirectionStatusCode, + 'url' => $result_array->redirectionUrl, + 'version' => $result_array->redirectionVersion, + 'data' => $result_array->redirectionData + ]; + } else { + return (object)[ + 'status' => 'KO', + 'code' => $result_array->redirectionStatusCode, + 'seal' => $result_array->seal + ]; + } + } else { + Log::critical(sprintf( + "MERCANET [%s] : redirectionStatusCode missing", + $this->parameters['transactionReference'] + )); + return (object)['status' => 'KO', 'code' => 500]; + } //end if + } +} diff --git a/app/Lib/Utils.php b/app/Lib/Utils.php index 54e2de77..4dcf94b0 100644 --- a/app/Lib/Utils.php +++ b/app/Lib/Utils.php @@ -6,7 +6,7 @@ use App\Models\Customer; use Illuminate\Support\Str; /** - * Some utilities methods + * Some utilities methods. */ // phpcs:disable Generic.Files.LineLength.TooLong class Utils @@ -111,7 +111,7 @@ class Utils } /** - * Multibyte strtr + * Multibyte strtr. * * @param string $str * @param string|array $from @@ -131,7 +131,7 @@ class Utils } /** - * Generate an unique pseudo + * Generate an unique pseudo. * * @param string $firstname * @param string $lastname diff --git a/app/Listeners/CreateNotification.php b/app/Listeners/CreateNotification.php index 59f6a5ac..3862b6d9 100644 --- a/app/Listeners/CreateNotification.php +++ b/app/Listeners/CreateNotification.php @@ -12,11 +12,11 @@ class CreateNotification * @param \App\Events\NewNotification $event * @return void */ - public function handle(NewNotification $event) + public function handle(NewNotification $event): void { $event->model->notifiable()->create([ - 'type' => $event->type, - 'content' => $event->content, + 'type' => $event->type, + 'content' => $event->content, 'customer_id' => $event->customer->id ?? null ]); } diff --git a/app/Listeners/PasswordHasReset.php b/app/Listeners/PasswordHasReset.php index ef397fd4..acd848bc 100644 --- a/app/Listeners/PasswordHasReset.php +++ b/app/Listeners/PasswordHasReset.php @@ -15,7 +15,7 @@ class PasswordHasReset * @param \Illuminate\Auth\Events\PasswordReset $event * @return void */ - public function handle(PasswordReset $event) + public function handle(PasswordReset $event): void { /** @var \App\Models\Customer */ $customer = $event->user; diff --git a/app/Mail/Arcadia/HasOrderedAnItem.php b/app/Mail/Arcadia/HasOrderedAnItem.php new file mode 100644 index 00000000..9076d39c --- /dev/null +++ b/app/Mail/Arcadia/HasOrderedAnItem.php @@ -0,0 +1,103 @@ +data = $data; + } + + /** + * Build the message. + * + * @return $this + * @throws \RuntimeException If generating XML fails. + */ + public function build() + { + $xml = $this->makeXml(); + + // * Format xml. + $doc = new \DOMDocument('1.0', 'utf-8'); + $doc->xmlStandalone = true; + $doc->preserveWhiteSpace = false; + $doc->formatOutput = true; + $doc->loadXML($xml); + $xml = $doc->saveXML(); + + if (!$xml) { + throw new \RuntimeException("Failed to make XML"); + } + + $fileName = Str::slug(sprintf( + 'order_%s_%s_%s%s', + $this->data->order_num, + ArcadiaOrderType::make($this->data->state_order)->label(), + date('Ymd_His'), + \microtime(true) + )) . '.xml'; + + Storage::disk('arcadia')->put($fileName, $xml); + + return $this->subject('Un object en vente à été commandé') + ->markdown('emails.arcadiaOrders.default_markdown') + ->attachData( + $xml, + $fileName, + ['mime' => 'application/xml'] + ); + } + + /** + * Render Arcadia xml for order. + * + * @return string + */ + private function makeXml(): string + { + return view('emails.arcadiaOrders.default_xml', [ + 'operation' => $this->data->operation, + 'support_code' => $this->data->support_code, + 'label_type_support' => $this->data->operation, + 'order_date' => $this->data->order_date, + 'order_num' => $this->data->order_num, + 'orderType' => ArcadiaOrderType::make($this->data->state_order), + 'package_unit_weight' => $this->data->package_unit_weight, + 'lastname' => $this->data->lastname, + 'firstname' => $this->data->firstname, + 'email' => $this->data->email, + 'address' => $this->data->address, + 'address2' => $this->data->address2, + 'city' => $this->data->city, + 'phone' => $this->data->phone, + 'postcode' => $this->data->postcode, + ])->render(); + } +} diff --git a/app/Mail/ContactComplainMail.php b/app/Mail/ContactComplainMail.php index d11142e8..5de65b02 100644 --- a/app/Mail/ContactComplainMail.php +++ b/app/Mail/ContactComplainMail.php @@ -7,7 +7,6 @@ use Illuminate\Bus\Queueable; use Illuminate\Mail\Attachment; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\File; -use Illuminate\Support\Facades\Storage; class ContactComplainMail extends ContactMail { @@ -17,16 +16,22 @@ class ContactComplainMail extends ContactMail /** * The order instance. * - * @var MailContactData + * @var \App\Models\MailContactData */ protected $data; + + /** + * The path of a specific file. + * + * @var string + */ protected $file; /** * Create a new message instance. * * @param \App\Models\MailContactData $data - * @param string|null $file + * @param string|null $file * @return void */ public function __construct(MailContactData $data, string|null $file) @@ -64,7 +69,7 @@ class ContactComplainMail extends ContactMail /** * Get the attachments for the message. * - * @return array + * @return array */ public function attachments(): array { diff --git a/app/Mail/ContactMail.php b/app/Mail/ContactMail.php index b287d9fe..b7d15de2 100644 --- a/app/Mail/ContactMail.php +++ b/app/Mail/ContactMail.php @@ -18,7 +18,7 @@ class ContactMail extends Mailable /** * The order instance. * - * @var MailContactData + * @var \App\Models\MailContactData */ protected $data; @@ -32,6 +32,7 @@ class ContactMail extends Mailable /** * Create a new message instance. * + * @param \App\Models\MailContactData $data * @return void */ public function __construct(MailContactData $data) @@ -63,14 +64,13 @@ class ContactMail extends Mailable */ protected function makeCsvFile(): string { - // we create the CSV into memory - // FILENAME + // Creation of the CSV into memory. $csv = Writer::createFromFileObject(new SplTempFileObject()); $csv->setDelimiter(';'); $csv->setEnclosure('"'); $csv->setOutputBOM(Bom::Utf8); - // we insert the CSV header + // Insert the CSV header. $csv->insertOne([ 'EMETTEUR_DATE', 'EMETTEUR_HEURE', diff --git a/app/Models/ActivityLog.php b/app/Models/ActivityLog.php index b7eb30e6..8932a01f 100644 --- a/app/Models/ActivityLog.php +++ b/app/Models/ActivityLog.php @@ -117,7 +117,6 @@ class ActivityLog extends Model }; } - /** * Get a value type as a string * ! Use this for display purpose only diff --git a/app/Models/BlockList.php b/app/Models/BlockList.php index 2bf9dbb0..47234cc4 100644 --- a/app/Models/BlockList.php +++ b/app/Models/BlockList.php @@ -19,7 +19,7 @@ class BlockList extends Model // * RELATIONSHIPS /** - * Summary of customer + * Get the associated customer who has blocked another one. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ @@ -29,7 +29,7 @@ class BlockList extends Model } /** - * + * Obtain the associated client that has been blocked by another one. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ diff --git a/app/Models/BrandAdvantage.php b/app/Models/BrandAdvantage.php index dcda11ef..db1e6a92 100644 --- a/app/Models/BrandAdvantage.php +++ b/app/Models/BrandAdvantage.php @@ -136,7 +136,7 @@ class BrandAdvantage extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedBrandAdvantagesOrderedByOrder'); self::getPublishedOrdered(); diff --git a/app/Models/ConsumptionMoment.php b/app/Models/ConsumptionMoment.php index 72e77091..4a1fd33d 100644 --- a/app/Models/ConsumptionMoment.php +++ b/app/Models/ConsumptionMoment.php @@ -72,7 +72,7 @@ class ConsumptionMoment extends Model // * METHODS /** - * Asserts using validation that the title is unique + * Asserts using validation that the title is unique. * * @param string|null $title * @param integer|null $id @@ -91,7 +91,7 @@ class ConsumptionMoment extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('consumptionMomentsOrderedBySlug'); self::getOrderedBySlug(); diff --git a/app/Models/Customer.php b/app/Models/Customer.php index a1e54c27..361a3144 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -100,7 +100,7 @@ class Customer extends Authenticatable implements MustVerifyEmail // * METHODS /** - * Asserts using validation that the email is unique + * Asserts using validation that the email is unique. * * @param mixed $id * @param mixed $email @@ -113,7 +113,7 @@ class Customer extends Authenticatable implements MustVerifyEmail } /** - * Asserts that the field is unique + * Asserts that the field is unique. * * @param mixed $id * @param string $field @@ -134,7 +134,7 @@ class Customer extends Authenticatable implements MustVerifyEmail } /** - * Get conversations with messages sorted by time + * Get conversations with messages sorted by time. * * @return array */ @@ -218,7 +218,7 @@ class Customer extends Authenticatable implements MustVerifyEmail * * @return void */ - public function sendEmailVerificationNotification() + public function sendEmailVerificationNotification(): void { $this->deleteIfDoesNotExistsInCrm(); $this->notify(new VerifyEmail()); @@ -230,7 +230,7 @@ class Customer extends Authenticatable implements MustVerifyEmail * @param mixed $token * @return void */ - public function sendPasswordResetNotification(mixed $token) + public function sendPasswordResetNotification(mixed $token): void { $this->deleteIfDoesNotExistsInCrm(); /** @var string $token */ @@ -296,7 +296,7 @@ class Customer extends Authenticatable implements MustVerifyEmail } /** - * Get api_data attribute, the crm Data of a customer + * Get api_data attribute, the crm Data of a customer. * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ @@ -348,7 +348,7 @@ class Customer extends Authenticatable implements MustVerifyEmail // * CRM CACHE /** - * Clear cached apiData + * Clear cached apiData. * * @return void */ @@ -360,24 +360,21 @@ class Customer extends Authenticatable implements MustVerifyEmail } /** - * Put customer crm part in cache + * Put customer crm part in cache. * - * @template T - * @param string $uuid The customer uuid. - * @param string $part_name The part name for the cache path. - * @param \Closure(void):T $part The part to cache. - * @param integer $ttl Time to live in seconds for the cache part. - * @return T - * @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint + * @param string $uuid The customer uuid. + * @param string $part_name The part name for the cache path. + * @param \Closure $part The part to cache. + * @param integer $ttl Time to live in seconds for the cache part. + * @return mixed */ - public static function cacheCrm(string $uuid, string $part_name, \Closure $part, int $ttl = 60 * 60) + public static function cacheCrm(string $uuid, string $part_name, \Closure $part, int $ttl = 60 * 60): mixed { - // phpcs:enable return Cache::remember("customers.{$uuid}.{$part_name}", $ttl, $part); } /** - * Clear customer crm cache part or totally + * Clear customer crm cache part or totally. * * @param string $uuid * @param string $part_name @@ -395,11 +392,11 @@ class Customer extends Authenticatable implements MustVerifyEmail /** * This mutate Customer attributes to array * including only non critical properties and crm values - * (One that can be send to front office without security breach) + * (One that can be send to front office without security breach). * * @return array */ - public function toArray() + public function toArray(): array { if ($this->hashcode) { return collect(array_merge( @@ -427,7 +424,7 @@ class Customer extends Authenticatable implements MustVerifyEmail // * RELATIONSHIPS /** - * Get all of the departaimant collections for the Customer + * Get all of the departaimant collections for the Customer. * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -437,7 +434,7 @@ class Customer extends Authenticatable implements MustVerifyEmail } /** - * The notifications that belong to the Customer + * The notifications that belong to the Customer. * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -447,7 +444,7 @@ class Customer extends Authenticatable implements MustVerifyEmail } /** - * The read notifications that belong to the Customer + * The read notifications that belong to the Customer. * * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ @@ -457,7 +454,7 @@ class Customer extends Authenticatable implements MustVerifyEmail } /** - * Get all of the messages for the Customer + * Get all of the messages for the Customer. * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -467,7 +464,7 @@ class Customer extends Authenticatable implements MustVerifyEmail } /** - * Get all of the sentMessages for the Customer + * Get all of the sentMessages for the Customer. * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ diff --git a/app/Models/DeliveryAddress.php b/app/Models/DeliveryAddress.php new file mode 100644 index 00000000..408c51c0 --- /dev/null +++ b/app/Models/DeliveryAddress.php @@ -0,0 +1,58 @@ + + */ + protected $fillable = [ + 'civility', + 'lastname', + 'firstname', + 'address', + 'postcode', + 'city', + 'phone' + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'civility' => ContactFormCivility::class + ]; + + // * RELATIONSHIPS + + /** + * Get the order that are assigned this delivery address. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function order(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(Order::class); + } +} diff --git a/app/Models/DemarcheSlide.php b/app/Models/DemarcheSlide.php index 32f4f451..c16e4d2b 100644 --- a/app/Models/DemarcheSlide.php +++ b/app/Models/DemarcheSlide.php @@ -127,7 +127,7 @@ class DemarcheSlide extends Model } /** - * Adjusts demarcheSlide status using current status and publish dates + * Adjusts demarcheSlide status using current status and publish dates. * * @param self $demarcheSlide * @return void @@ -151,7 +151,7 @@ class DemarcheSlide extends Model } /** - * Getter the current status following planification dates planificated_status + * Getter the current status following planification dates planificated_status. * * @return boolean */ @@ -189,7 +189,7 @@ class DemarcheSlide extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedDemarcheSlidesOrderedByOrder'); self::getPublishedOrdered(); diff --git a/app/Models/DepartaimantsCollection.php b/app/Models/DepartaimantsCollection.php index c6d3ebdd..107b00f3 100644 --- a/app/Models/DepartaimantsCollection.php +++ b/app/Models/DepartaimantsCollection.php @@ -13,7 +13,7 @@ use Illuminate\Database\Eloquent\Model; /** * Departaimants Model - * Has functions to store departaimants on multiple statuses + * Has functions to store departaimants on multiple statuses. * * @property \App\Enums\Departaimants\DepartaimantCollectionType $type * @property integer $customer_id @@ -26,7 +26,6 @@ use Illuminate\Database\Eloquent\Model; * @uses App\Enums\Departaimant * @uses App\Enums\DepartaimantStatus * @uses App\Enums\DepartaimantCollectionType - * @author Jérémy Munsch */ class DepartaimantsCollection extends Model { @@ -67,21 +66,21 @@ class DepartaimantsCollection extends Model ]; /** - * Columns prefix + * Columns prefix. * * @var string */ private static $columnTableNamePrefix = 'col'; /** - * Le prefix d'un table departaimant + * Le prefix d'un table departaimant. * * @var integer */ private static $tableFields = 4; /** - * Le nombre de colonnes des departaimants + * Le nombre de colonnes des departaimants. * * * La largeur en bits d'un départaimant * * to be set in construct @@ -131,7 +130,7 @@ class DepartaimantsCollection extends Model // * RELATIONSHIPS /** - * Get the customer that owns the DepartaimantsCollection + * Get the customer that owns the DepartaimantsCollection. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ @@ -143,7 +142,7 @@ class DepartaimantsCollection extends Model // * METHODS /** - * Add a Departaiment to this collection + * Add a Departaiment to this collection. * * @param \App\Enums\Departaimants\IDepartaimant $departaimant * @return void @@ -154,7 +153,7 @@ class DepartaimantsCollection extends Model } /** - * Removes a Departaime from this collection + * Removes a Departaime from this collection. * * @param \App\Enums\Departaimants\IDepartaimant $departaimant * @return void @@ -165,7 +164,7 @@ class DepartaimantsCollection extends Model } /** - * Checks if this collection has a Departaimant + * Checks if this collection has a Departaimant. * * @param \App\Enums\Departaimants\IDepartaimant $departaimant * @return boolean @@ -176,7 +175,7 @@ class DepartaimantsCollection extends Model } /** - * Set the Departaimant to a specific status + * Set the Departaimant to a specific status. * * @param \App\Enums\Departaimants\IDepartaimant $departaimant * @param \App\Enums\Departaimants\DepartaimantStatus $status @@ -192,7 +191,7 @@ class DepartaimantsCollection extends Model } /** - * Test the Departaimant status + * Test the Departaimant status. * * @param \App\Enums\Departaimants\IDepartaimant $departaimant * @param \App\Enums\Departaimants\DepartaimantStatus $status @@ -213,7 +212,7 @@ class DepartaimantsCollection extends Model } /** - * Get all Customer which has their departaimant with a status + * Get all Customer which has their departaimant with a status. * * @param \App\Enums\Departaimants\DepartaimantCollectionType $depColtype * @param \App\Enums\Departaimants\IDepartaimant $departaimant @@ -241,7 +240,7 @@ class DepartaimantsCollection extends Model } /** - * Get all Customer which has their departaimant with a status + * Get all Customer which has their departaimant with a status. * * @param array $departaimants * @param \App\Enums\Departaimants\DepartaimantStatus $status @@ -279,7 +278,7 @@ class DepartaimantsCollection extends Model } /** - * Get the Departaimant status + * Get the Departaimant status. * * @param \App\Enums\Departaimants\IDepartaimant $departaimant * @return \App\Enums\Departaimants\DepartaimantStatus @@ -299,7 +298,7 @@ class DepartaimantsCollection extends Model } /** - * Get a list of all the departaimants and their status + * Get a list of all the departaimants and their status. * * @uses \App\Enums\Departaimants\IDepartaimant * @uses \App\Enums\Departaimants\DepartaimantStatus @@ -323,7 +322,7 @@ class DepartaimantsCollection extends Model } /** - * Get a list of all the departaimants and their status + * Get a list of all the departaimants and their status. * * @param \App\Enums\Departaimants\DepartaimantCollectionType $depColtype * @param \App\Enums\Departaimants\DepartaimantStatus $status @@ -353,7 +352,7 @@ class DepartaimantsCollection extends Model } /** - * Set the Departaimant column to 0 + * Set the Departaimant column to 0. * * @param \App\Enums\Departaimants\IDepartaimant $departaimant * @return void @@ -367,8 +366,7 @@ class DepartaimantsCollection extends Model } /** - * Donne la 'table' (colonne SQL) dans laquelle se trouve - * le département + * Donne la 'table' (colonne SQL) dans laquelle se trouve le département. * * @param \App\Enums\Departaimants\DepartaimantCollectionType $depColtype * @param \App\Enums\Departaimants\IDepartaimant $dep @@ -387,7 +385,7 @@ class DepartaimantsCollection extends Model /** * Donne la colonne (binaire) dans laquelle se trouve le département - * Dans sa 'table' (colonne SQL) + * dans sa 'table' (colonne SQL). * * @param \App\Enums\Departaimants\DepartaimantCollectionType $depColtype * @param \App\Enums\Departaimants\IDepartaimant $dep @@ -408,7 +406,7 @@ class DepartaimantsCollection extends Model } /** - * Donne la valeur (masque binaire) d'un département + * Donne la valeur (masque binaire) d'un département. * * @param \App\Enums\Departaimants\IDepartaimant $dep * @return integer @@ -419,7 +417,7 @@ class DepartaimantsCollection extends Model } /** - * Genère la liste des departements avec leur masques + * Genère la liste des departements avec leur masques. * * @param \App\Enums\Departaimants\DepartaimantCollectionType $depColtype * @return array @@ -440,7 +438,7 @@ class DepartaimantsCollection extends Model /** * Get the with in bit length for a column - * that can be used to store data + * that can be used to store data. * * @return float */ @@ -451,7 +449,7 @@ class DepartaimantsCollection extends Model } /** - * Get departaimant enum values + * Get departaimant enum values. * * @param \App\Enums\Departaimants\DepartaimantCollectionType $depColtype * @return array @@ -463,7 +461,7 @@ class DepartaimantsCollection extends Model } /** - * Get a departaiment enum from a collection type + * Get a departaiment enum from a collection type. * * @param \App\Enums\Departaimants\DepartaimantCollectionType $depColtype * @return string diff --git a/app/Models/Discount.php b/app/Models/Discount.php index 3c9852e0..f8c7b32e 100644 --- a/app/Models/Discount.php +++ b/app/Models/Discount.php @@ -112,7 +112,6 @@ class Discount extends Model /** * Static method to fetch br List. - * * ! see fetchActiveBr for output object definition * * @return object|null diff --git a/app/Models/GeneralSetting.php b/app/Models/GeneralSetting.php index bca9e942..a3c4f2dc 100644 --- a/app/Models/GeneralSetting.php +++ b/app/Models/GeneralSetting.php @@ -93,7 +93,7 @@ class GeneralSetting extends Model * * @return mixed */ - public function getRouteKey() + public function getRouteKey(): mixed { /** @var \App\Enums\StaticPageType */ $enum = $this->getAttribute($this->getRouteKeyName()); @@ -115,11 +115,11 @@ class GeneralSetting extends Model // * GETTERS /** - * Mutator for 'value' attribute + * Mutator for 'value' attribute. * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ - public function value(): Attribute + public function value(): \Illuminate\Database\Eloquent\Casts\Attribute { return Attribute::make( get: fn($value): array => self::valueFromJson($this->type, $value), @@ -170,7 +170,7 @@ class GeneralSetting extends Model } /** - * Unserialize setting value + * Unserialize setting value. * * @param \App\Enums\Settings\SettingsType $type * @param string $json @@ -197,7 +197,7 @@ class GeneralSetting extends Model * @param \App\Enums\Settings\SettingsType $type * @return void */ - private static function resetCache(SettingsType $type) + private static function resetCache(SettingsType $type): void { Cache::forget(sprintf('%sGeneralSetting', $type->name)); self::getCachedGeneralSetting($type); @@ -207,9 +207,9 @@ class GeneralSetting extends Model * Get cached general setting associated to a given type. * * @param \App\Enums\Settings\SettingsType $type - * @return \App\Models\StaticPage + * @return mixed */ - public static function getCachedGeneralSetting(SettingsType $type) + public static function getCachedGeneralSetting(SettingsType $type): mixed { return Cache::rememberForever( sprintf('%sGeneralSetting', $type->name), diff --git a/app/Models/HistoricalMilestone.php b/app/Models/HistoricalMilestone.php index e860d75c..3c0109c6 100644 --- a/app/Models/HistoricalMilestone.php +++ b/app/Models/HistoricalMilestone.php @@ -117,7 +117,7 @@ class HistoricalMilestone extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedHistoricalMilestonesOrdered'); self::getPublishedOrdered(); diff --git a/app/Models/KeyNumber.php b/app/Models/KeyNumber.php index fa619bd5..4eb9fe09 100644 --- a/app/Models/KeyNumber.php +++ b/app/Models/KeyNumber.php @@ -84,7 +84,7 @@ class KeyNumber extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('5publishedKeyNumbersOrderedByOrder'); self::get5PublishedOrdered(); diff --git a/app/Models/MailContactData.php b/app/Models/MailContactData.php index 25b673f3..d24c83af 100644 --- a/app/Models/MailContactData.php +++ b/app/Models/MailContactData.php @@ -8,7 +8,32 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; /** - * This class is used to store data in memory only, not in databases + * This class is used to store data in memory only, not in databases. + * + * @property \App\Enums\ContactFormCivility $civility Picto path. + * @property string $lastname Picto title attribute. + * @property string $firstname Picto alt attribute. + * @property string $address Legend of picto. + * @property string $email The origin of the text. + * @property string $postal Change order request. + * @property string $town Picto publish status. + * @property integer $phone Picto publish status. + * @property \App\Enums\ContactFormType $type Picto publish status. + * @property string $onepagesubject Picto publish status. + * @property string $message Picto publish status. + * @property string $joinFile Picto publish status. + * @property string $productName Picto publish status. + * @property string $productCode Picto publish status. + * @property string $buyPlace Picto publish status. + * @property string $lotNumberName Picto publish status. + * @property string $limitDateDay Picto publish status. + * @property string $limitDateMonth Picto publish status. + * @property string $limitDateYear Picto publish status. + * @property string $consumptionDateDay Picto publish status. + * @property string $consumptionDateMonth Picto publish status. + * @property string $consumptionDateYear Picto publish status. + * @property-read \Illuminate\Support\Carbon $created_at Created date. + * @property-read \Illuminate\Support\Carbon $updated_at Updated date. */ class MailContactData extends Model { @@ -22,7 +47,7 @@ class MailContactData extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ 'civility', @@ -55,31 +80,30 @@ class MailContactData extends Model * @var array */ protected $casts = [ - 'type' => ContactFormType::class, + 'type' => ContactFormType::class, 'civility' => ContactFormCivility::class ]; - /** * The model's attributes. * * @var array */ protected $attributes = [ - 'productName' => '', - 'productCode' => '', - 'buyPlace' => '', - 'lotNumberName' => 0, - 'limitDateDay' => '00', - 'limitDateMonth' => '00', - 'limitDateYear' => '0000', - 'consumptionDateDay' => '00', + 'productName' => '', + 'productCode' => '', + 'buyPlace' => '', + 'lotNumberName' => 0, + 'limitDateDay' => '00', + 'limitDateMonth' => '00', + 'limitDateYear' => '0000', + 'consumptionDateDay' => '00', 'consumptionDateMonth' => '00', - 'consumptionDateYear' => '0000', + 'consumptionDateYear' => '0000', ]; /** - * Export the model data to a Formatted Arcadia Data array + * Export the model data to a Formatted Arcadia Data array. * * @return array */ @@ -105,7 +129,7 @@ class MailContactData extends Model } /** - * Export the model data to a Formatted Passerelle Data array + * Export the model data to a Formatted Passerelle Data array. * * * Removed * 'EMETTEUR_OBJETDEMANDE' => "objet de la reclamation string", @@ -119,56 +143,45 @@ class MailContactData extends Model { // * CF PAsserrelle fields .csv return [ - // * Marque le Gaulois (Obligatoire) - 'EMETTEUR_MARQUE' => 1, - // * Obligatoire - 'EMETTEUR_DATE' => date("Y-m-d"), - // * Obligatoire - 'EMETTEUR_HEURE' => date("h:i:s"), - 'EMETTEUR_CIVILITE' => $this->civility->value, - // * Obligatoire Limité a 50 - 'EMETTEUR_NOM' => Str::limit($this->lastname, 50, ''), - // * Obligatoire - 'EMETTEUR_PRENOM' => $this->firstname, - 'EMETTEUR_ADRESSE1' => $this->address, - 'EMETTEUR_ADRESSE2' => '', - 'EMETTEUR_CP' => $this->postal, - // * Obligatoire - 'EMETTEUR_VILLE' => $this->town, - 'EMETTEUR_EMAIL' => $this->email, - 'EMETTEUR_TELEPHONE' => $this->phone, - 'EMETTEUR_PAYS' => 'France', - 'EMETTEUR_NOM_PRODUIT' => $this->productName, - // * Obligatoire - 'EMETTEUR_EAN13' => $this->productCode, + // Le Gaulois brand. + 'EMETTEUR_MARQUE' => 1, + 'EMETTEUR_DATE' => date("Y-m-d"), + 'EMETTEUR_HEURE' => date("h:i:s"), + 'EMETTEUR_CIVILITE' => $this->civility->value, + // Limited at 50 characters. + 'EMETTEUR_NOM' => Str::limit($this->lastname, 50, ''), + 'EMETTEUR_PRENOM' => $this->firstname, + 'EMETTEUR_ADRESSE1' => $this->address, + 'EMETTEUR_ADRESSE2' => '', + 'EMETTEUR_CP' => $this->postal, + 'EMETTEUR_VILLE' => $this->town, + 'EMETTEUR_EMAIL' => $this->email, + 'EMETTEUR_TELEPHONE' => $this->phone, + 'EMETTEUR_PAYS' => 'France', + 'EMETTEUR_NOM_PRODUIT' => $this->productName, + 'EMETTEUR_EAN13' => $this->productCode, 'EMETTEUR_DATE_LIMITE_CONSO' => sprintf( '%s/%s/%s', $this->limitDateDay ?? '00', $this->limitDateMonth ?? '00', $this->limitDateYear ?? '0000' ), - 'EMETTEUR_NUM_LOT' => $this->lotNumberName, - 'EMETTEUR_MESSAGE' => $this->type->equals(ContactFormType::nl) ? + 'EMETTEUR_NUM_LOT' => $this->lotNumberName, + 'EMETTEUR_MESSAGE' => $this->type->equals(ContactFormType::nl) ? sprintf('%s \n %s', $this->onepagesubject, $this->message) : $this->message, - 'EMETTEUR_PJ' => $this->joinFile, - 'DATE_CONSO' => sprintf( + 'EMETTEUR_PJ' => $this->joinFile, + 'DATE_CONSO' => sprintf( '%s/%s/%s', $this->consumptionDateDay ?? '00', $this->consumptionDateMonth ?? '00', $this->consumptionDateYear ?? '0000' ), - 'LIEU_ACHAT' => $this->buyPlace, - - // * FUCK passerelle et leur DTD qui n'existe pas cf: resources/documents - // * Recla conso ? - 'EMETTEUR_OBJETDEMANDE' => 3, - // * doit être vide ? - 'EMETTEUR_OPT_IN' => '', - // * c'est quoi ça ? - 'REPONSE_ETAT' => 0, - // * ok - 'TYPE_REPONSE' => 'mail' + 'LIEU_ACHAT' => $this->buyPlace, + 'EMETTEUR_OBJETDEMANDE' => 3, + 'EMETTEUR_OPT_IN' => '', + 'REPONSE_ETAT' => 0, + 'TYPE_REPONSE' => 'mail' ]; } } diff --git a/app/Models/MainSlide.php b/app/Models/MainSlide.php index 6bb9325e..0c5e440f 100644 --- a/app/Models/MainSlide.php +++ b/app/Models/MainSlide.php @@ -177,7 +177,7 @@ class MainSlide extends Model } /** - * Adjusts mainSlide status using current status and publish dates + * Adjusts mainSlide status using current status and publish dates. * * @param self $mainSlide * @return void @@ -201,7 +201,7 @@ class MainSlide extends Model } /** - * Getter the current status following planification dates planificated_status + * Getter the current status following planification dates planificated_status. * * @return boolean */ @@ -239,7 +239,7 @@ class MainSlide extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedMainSlidesOrderedByOrderWithApproaches'); self::getPublishedOrderedWithApproaches(); diff --git a/app/Models/Meat.php b/app/Models/Meat.php index b70cd7bf..8d6bff60 100644 --- a/app/Models/Meat.php +++ b/app/Models/Meat.php @@ -127,14 +127,14 @@ class Meat extends Model } /** - * Asserts using validation that the label is unique + * Asserts using validation that the label is unique. * * @param string|null $label * @param integer|null $id * @return void * @throws \Illuminate\Validation\ValidationException If a meat label already exists. */ - private static function assertLabelIsUnique(string|null $label, ?int $id = null) + private static function assertLabelIsUnique(string|null $label, ?int $id = null): void { $table = (new self())->getTable(); ToolboxHelper::assertFieldIsUnique($table, 'label', $label, $id); @@ -157,7 +157,7 @@ class Meat extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedMeatsOrderedByOrder'); self::getPublishedOrdered(); diff --git a/app/Models/MenuNodeData.php b/app/Models/MenuNodeData.php index 6c538179..3063114d 100644 --- a/app/Models/MenuNodeData.php +++ b/app/Models/MenuNodeData.php @@ -116,7 +116,7 @@ class MenuNodeData extends Model // * METHODS /** - * Deny Handling some of the MenuNodeData + * Deny Handling some of the MenuNodeData. * * @param self $model * @return void diff --git a/app/Models/Message.php b/app/Models/Message.php index c2d89390..4b046e05 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -32,7 +32,7 @@ class Message extends Model implements INotifiable // * RELATIONSHIPS /** - * BelongsTo customer relation + * BelongsTo customer relation. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ @@ -42,7 +42,7 @@ class Message extends Model implements INotifiable } /** - * BelongsTo customer relation + * BelongsTo customer relation. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ @@ -52,7 +52,7 @@ class Message extends Model implements INotifiable } /** - * MorphOne notification relation + * MorphOne notification relation. * * @return \Illuminate\Database\Eloquent\Relations\MorphOne */ @@ -64,19 +64,19 @@ class Message extends Model implements INotifiable // * METHODS /** - * created_at_nice_date attribute + * Created at nice date. * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ public function createdAtNiceDate(): \Illuminate\Database\Eloquent\Casts\Attribute { return Attribute::make( - get: fn () => (new NiceDate())->get(null, '', $this->created_at, []) + get: fn() => (new NiceDate())->get(null, '', $this->created_at, []) ); } /** - * Get MessagesOlderThan attribute + * Get older messages. * * @param integer $days * @return \Illuminate\Contracts\Database\Eloquent\Builder diff --git a/app/Models/News.php b/app/Models/News.php index 4e6d3f81..7629bfdc 100644 --- a/app/Models/News.php +++ b/app/Models/News.php @@ -222,7 +222,7 @@ class News extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedNewsWithCategories'); self::getPublishedWithNewsCategory(); diff --git a/app/Models/NewsBreeding.php b/app/Models/NewsBreeding.php index 0666082a..d023cb57 100644 --- a/app/Models/NewsBreeding.php +++ b/app/Models/NewsBreeding.php @@ -121,7 +121,7 @@ class NewsBreeding extends Model } /** - * Adjusts breedingNews status using current status and publish dates + * Adjusts breedingNews status using current status and publish dates. * * @param self $model * @return void @@ -145,7 +145,7 @@ class NewsBreeding extends Model } /** - * Asserts using validation that the title is unique + * Asserts using validation that the title is unique. * * @param string|null $title * @param integer|null $id @@ -161,7 +161,7 @@ class NewsBreeding extends Model /** - * Getter the current status following planification dates planificated_status + * Getter the current status following planification dates planificated_status. * * @return boolean */ @@ -189,7 +189,7 @@ class NewsBreeding extends Model * @param \App\Enums\StaticPageType $type * @return void */ - private static function resetCache(StaticPageType $type) + private static function resetCache(StaticPageType $type): void { Cache::forget(sprintf('%sPublishedNewsBreedingOrderedByOrder', $type->name)); self::getPublishedOrderedForType($type); diff --git a/app/Models/NewsCategory.php b/app/Models/NewsCategory.php index 8802345c..07dd9366 100644 --- a/app/Models/NewsCategory.php +++ b/app/Models/NewsCategory.php @@ -101,7 +101,7 @@ class NewsCategory extends Model } /** - * Asserts using validation that the title is unique + * Asserts using validation that the title is unique. * * @param string|null $title * @param integer|null $id @@ -120,7 +120,7 @@ class NewsCategory extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedNewsCategoriesOrderedByOrder'); self::getPublishedOrdered(); diff --git a/app/Models/NewsClub.php b/app/Models/NewsClub.php index f13be09a..a06c874c 100644 --- a/app/Models/NewsClub.php +++ b/app/Models/NewsClub.php @@ -2,7 +2,7 @@ namespace App\Models; -use App\Enums\Club\NewsClubTypeEnum; +use App\Enums\News\NewsClubTypeEnum; use App\Lib\Helpers\FileStorageHelper; use App\Traits\Models\ActivityLog; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -11,7 +11,7 @@ use Illuminate\Support\Str; use App\Lib\Helpers\ToolboxHelper; /** - * @property \App\Enums\Club\NewsClubTypeEnum $type News club type. + * @property \App\Enums\News\NewsClubTypeEnum $type News club type. * @property string $title News club title. * @property string $slug News club title slug. * @property string|null $content News club content. @@ -96,7 +96,7 @@ class NewsClub extends Model } /** - * Asserts using validation that the title is unique + * Asserts using validation that the title is unique. * * @param string|null $title * @param integer|null $id diff --git a/app/Models/NewsCorporate.php b/app/Models/NewsCorporate.php index ac4e8510..6e47129f 100644 --- a/app/Models/NewsCorporate.php +++ b/app/Models/NewsCorporate.php @@ -85,7 +85,7 @@ class NewsCorporate extends Model // * METHODS /** - * Adjusts newsCorporate status using current status and publish dates + * Adjusts newsCorporate status using current status and publish dates. * * @param self $newsCorporate * @return void @@ -109,7 +109,7 @@ class NewsCorporate extends Model } /** - * Getter the current status following planification dates planificated_status + * Getter the current status following planification dates planificated_status. * * @return boolean */ @@ -136,7 +136,7 @@ class NewsCorporate extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('9publishedNewsCorporateOrderedByUpdatedAt'); self::get9PublishedOrderedByUpdatedAt(); @@ -150,11 +150,7 @@ class NewsCorporate extends Model public static function get9PublishedOrderedByUpdatedAt(): \Illuminate\Database\Eloquent\Collection { return Cache::rememberForever('9publishedNewsCorporateOrderedByUpdatedAt', function () { - return self::query() - ->where('published', true) - ->orderByDesc('updated_at') - ->limit(9) - ->get(); + return self::query()->where('published', true)->orderByDesc('updated_at')->limit(9)->get(); }); } } diff --git a/app/Models/Notification.php b/app/Models/Notification.php index 7f1a5080..edb558fa 100644 --- a/app/Models/Notification.php +++ b/app/Models/Notification.php @@ -6,6 +6,8 @@ use App\Casts\NiceDate; use App\Enums\NotificationType; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\View; /** * @property \App\Enums\NotificationType $type Notification type. @@ -38,10 +40,41 @@ class Notification extends Model 'type' => NotificationType::class ]; + // * METHODS + + /** + * Created at nice date. + * + * @return \Illuminate\Database\Eloquent\Casts\Attribute + */ + public function createdAtNiceDate(): \Illuminate\Database\Eloquent\Casts\Attribute + { + return Attribute::make( + get: fn() => (new NiceDate())->get(null, '', $this->created_at, []) + ); + } + + /** + * Get notifications. + * + * @return void + */ + public static function getNotifications(): void + { + if (!Auth::guard('club')->check()) { + return; + } + /** @var \App\Models\Customer $customerModel */ + $customerModel = Auth::guard('club')->user(); + $notifications = $customerModel->getAllNotifications(); + View::share('notifications', count($notifications) - $customerModel->readNotifications()->count()); + View::share('notificationsList', $notifications); + } + // * RELATIONSHIPS /** - * Get the customer that owns the Notifications + * Get the customer that owns the Notifications. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ @@ -51,7 +84,7 @@ class Notification extends Model } /** - * Polymorphic relation : Get the parent model + * Polymorphic relation : Get the parent model. * * @return \Illuminate\Database\Eloquent\Relations\MorphTo */ @@ -59,18 +92,4 @@ class Notification extends Model { return $this->morphTo(); } - - // * METHODS - - /** - * created_at_nice_date attribute - * - * @return \Illuminate\Database\Eloquent\Casts\Attribute - */ - public function createdAtNiceDate(): \Illuminate\Database\Eloquent\Casts\Attribute - { - return Attribute::make( - get: fn () => (new NiceDate())->get(null, '', $this->created_at, []) - ); - } } diff --git a/app/Models/Order.php b/app/Models/Order.php new file mode 100644 index 00000000..35be826c --- /dev/null +++ b/app/Models/Order.php @@ -0,0 +1,117 @@ + + */ + protected $fillable = [ + 'firstname', + 'lastname', + 'address', + 'postcode', + 'phone', + 'email', + 'city', + 'civility', + 'status', + 'payementType', + 'customer_id', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'civility' => ContactFormCivility::class, + 'status' => OrderStatus::class, + 'payementType' => OrderType::class + ]; + + // * METHODS + + /** + * Export the model data to a Formatted Arcadia Data array. + * + * @return array> + */ + public function exportToOrderMailData(): array + { + return [ + 'ref' => $this->ref, + 'civility' => $this->civility->value(), + 'lastname' => $this->lastname, + 'firstname' => $this->firstname, + 'address' => $this->address, + 'postcode' => $this->postcode, + 'city' => $this->city, + 'email' => $this->email, + 'phone' => $this->phone ?? '', + 'items' => $this->sellItems()->withPivot('quantity')->get()->map(function (SellItem $item) { + // @phpstan-ignore-next-line + $item->typeCustom = $item->type->toArray()[$item->type->name()]; + return $item; + })->toArray() + ]; + } + + // * RELATIONSHIPS + + /** + * Get all of the items that are assigned this order. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function sellItems(): \Illuminate\Database\Eloquent\Relations\BelongsToMany + { + return $this->belongsToMany(SellItem::class)->withPivot('quantity'); + } + + /** + * Get the customer that are assigned this order. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function customer(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(Customer::class); + } + + /** + * Get the delivery address that are assigned this order. + * + * @return \Illuminate\Database\Eloquent\Relations\HasOne + */ + public function deliveryAddress(): \Illuminate\Database\Eloquent\Relations\HasOne + { + return $this->hasOne(DeliveryAddress::class); + } +} diff --git a/app/Models/OrderSellItem.php b/app/Models/OrderSellItem.php new file mode 100644 index 00000000..aa6c3122 --- /dev/null +++ b/app/Models/OrderSellItem.php @@ -0,0 +1,9 @@ +getTable(); ToolboxHelper::assertFieldIsUnique($table, 'label', $label, $id); @@ -200,7 +200,7 @@ class Product extends Model implements SeoInterface * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedDiscoveredProducts'); self::getPublishedDiscovered(); diff --git a/app/Models/ProductType.php b/app/Models/ProductType.php index f0e1a808..71ce48e1 100644 --- a/app/Models/ProductType.php +++ b/app/Models/ProductType.php @@ -79,7 +79,7 @@ class ProductType extends Model // * METHODS /** - * Asserts using validation that the label is unique + * Asserts using validation that the label is unique. * * @param string|null $label * @param integer|null $id diff --git a/app/Models/Range.php b/app/Models/Range.php index bc4dc913..5e6c0206 100644 --- a/app/Models/Range.php +++ b/app/Models/Range.php @@ -132,14 +132,14 @@ class Range extends Model } /** - * Asserts using validation that the label is unique + * Asserts using validation that the label is unique. * * @param string|null $label * @param integer|null $id * @return void * @throws \Illuminate\Validation\ValidationException If a range label already exists. */ - private static function assertLabelIsUnique(string|null $label, ?int $id = null) + private static function assertLabelIsUnique(string|null $label, ?int $id = null): void { $table = (new self())->getTable(); ToolboxHelper::assertFieldIsUnique($table, 'label', $label, $id); @@ -162,7 +162,7 @@ class Range extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedRangesOrderedByOrder'); self::getPublishedOrdered(); diff --git a/app/Models/Recipe.php b/app/Models/Recipe.php index 60806c9d..8100ab8c 100644 --- a/app/Models/Recipe.php +++ b/app/Models/Recipe.php @@ -127,14 +127,14 @@ class Recipe extends Model implements SeoInterface } /** - * Asserts using validation that the label is unique + * Asserts using validation that the label is unique. * * @param string|null $title * @param integer|null $id * @return void * @throws \Illuminate\Validation\ValidationException If a range title already exists. */ - private static function assertTitleIsUnique(string|null $title, ?int $id = null) + private static function assertTitleIsUnique(string|null $title, ?int $id = null): void { $table = (new self())->getTable(); ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id); @@ -198,7 +198,7 @@ class Recipe extends Model implements SeoInterface * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedFirstHighlightedRecipe'); self::getFirstHighlighted(); diff --git a/app/Models/SellItem.php b/app/Models/SellItem.php new file mode 100644 index 00000000..e0884f3f --- /dev/null +++ b/app/Models/SellItem.php @@ -0,0 +1,111 @@ + + */ + protected $fillable = [ + 'name', + 'type', + 'picture', + 'picture_alt', + 'picture_title', + 'price', + 'arcadia_id', + 'arcadia_weight', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'type' => SellItemsType::class, + ]; + + /** + * Perform any actions required after the model boots. + * + * @return void + */ + protected static function booted(): void + { + static::creating(function (self $model) { + self::setOrder($model); + $model->picture = FileStorageHelper::storeFile($model, $model->picture, true); + }); + static::updating(function (self $model) { + $model->picture = FileStorageHelper::storeFile($model, $model->picture, true); + }); + static::updated(function (self $model) { + FileStorageHelper::removeOldFile($model, 'picture'); + }); + static::deleted(function (self $model) { + FileStorageHelper::removeFile($model, 'picture'); + }); + } + + // * METHODS + + /** + * Set model's order after the last element of the list. + * + * @param self $mainSlide + * @return void + */ + private static function setOrder(self $mainSlide): void + { + $mainSlide->order = \intval(self::query()->max('order')) + 1; + } + + /** + * Getter html_name. + * + * @return string + */ + public function getHtmlNameAttribute(): string + { + return nl2br(strip_tags($this->name)); + } + + /** + * Convert the model instance to an array. + * + * @return array> + */ + public function toArray(): array + { + return array_merge(parent::toArray(), [ + 'htmlName' => $this->htmlName + ]); + } +} diff --git a/app/Models/StaticPage.php b/app/Models/StaticPage.php index 88934219..95935263 100644 --- a/app/Models/StaticPage.php +++ b/app/Models/StaticPage.php @@ -96,7 +96,7 @@ class StaticPage extends Model * * @return mixed */ - public function getRouteKey() + public function getRouteKey(): mixed { /** @var \App\Enums\StaticPageType */ $enum = $this->getAttribute($this->getRouteKeyName()); @@ -137,7 +137,7 @@ class StaticPage extends Model /** * Asserts using validation that the title is unique * - * @param string|null $seo_title + * @param string|null $title * @param integer|null $id * @return void * @throws \Illuminate\Validation\ValidationException If an actu seo_title already exists. @@ -157,7 +157,7 @@ class StaticPage extends Model * @param \App\Enums\StaticPageType $type * @return void */ - private static function resetCache(StaticPageType $type) + private static function resetCache(StaticPageType $type): void { Cache::forget(sprintf('%sStaticPage', $type->name)); self::getCachedStaticPage($type); @@ -173,7 +173,7 @@ class StaticPage extends Model * @param \App\Enums\StaticPageType $type * @return \App\Models\StaticPage */ - public static function getCachedStaticPage(StaticPageType $type) + public static function getCachedStaticPage(StaticPageType $type): \App\Models\StaticPage { return Cache::rememberForever( sprintf('%sStaticPage', $type->name), @@ -186,7 +186,7 @@ class StaticPage extends Model * * @return \Illuminate\Database\Eloquent\Collection */ - public static function getCachedBreedingStaticPages() + public static function getCachedBreedingStaticPages(): \Illuminate\Database\Eloquent\Collection { return Cache::rememberForever( 'breedingStaticPages', diff --git a/app/Models/Submeat.php b/app/Models/Submeat.php index 9662b818..8c003cce 100644 --- a/app/Models/Submeat.php +++ b/app/Models/Submeat.php @@ -133,7 +133,7 @@ class Submeat extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedSubmeatsOrderedByOrder'); self::getPublishedOrdered(); diff --git a/app/Models/Subrange.php b/app/Models/Subrange.php index a9558e05..e37f92fc 100644 --- a/app/Models/Subrange.php +++ b/app/Models/Subrange.php @@ -132,7 +132,7 @@ class Subrange extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedSubrangesOrderedByOrder'); self::getPublishedOrdered(); diff --git a/app/Models/Testimonial.php b/app/Models/Testimonial.php index 79b787ec..4e09f2cf 100644 --- a/app/Models/Testimonial.php +++ b/app/Models/Testimonial.php @@ -115,7 +115,7 @@ class Testimonial extends Model * @param integer $max * @return void */ - private static function checkPublishedCount(self $model, int $max) + private static function checkPublishedCount(self $model, int $max): void { Validator::make(['published' => $model->published], [ 'published' => Rule::prohibitedIf( @@ -137,7 +137,7 @@ class Testimonial extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('publishedTestimonialsOrderedByOrder'); self::getPublishedOrdered(); diff --git a/app/Models/User.php b/app/Models/User.php index c41a87b6..437b113a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -16,7 +16,7 @@ use Illuminate\Notifications\Notifiable; * @property string $remember_token Remember_token. * @property \App\Enums\Users\RoleEnum $role Role. * @property \Illuminate\Support\Carbon $last_connection Last_connection. - * @property \Illuminate\Support\Carbon $published Published status. + * @property boolean $published Published status. * @property \Illuminate\Support\Carbon $published_at Publication status update date. * @property-read \Illuminate\Support\Carbon $created_at Created date. * @property-read \Illuminate\Support\Carbon $updated_at Updated date. diff --git a/app/Models/Video.php b/app/Models/Video.php index eb637122..d81e8341 100644 --- a/app/Models/Video.php +++ b/app/Models/Video.php @@ -16,8 +16,6 @@ use Illuminate\Support\Facades\Cache; * @property boolean $is_on_homepage Is the video on the home page? * @property-read \Illuminate\Support\Carbon $created_at Created date. * @property-read \Illuminate\Support\Carbon $updated_at Updated date. - * - * @property-read \App\Models\Product $product Product associated to the video. . */ class Video extends Model { @@ -87,7 +85,7 @@ class Video extends Model * * @return void */ - private static function resetCache() + private static function resetCache(): void { Cache::forget('9publishedHomeVideosOrderedByOrderWithProducts'); self::get9PublishedHomeVideosOrderedWithProducts(); diff --git a/app/Notifications/Customer/DestroyCustomer.php b/app/Notifications/Customer/DestroyCustomer.php index db830229..95799ca7 100644 --- a/app/Notifications/Customer/DestroyCustomer.php +++ b/app/Notifications/Customer/DestroyCustomer.php @@ -10,13 +10,10 @@ class DestroyCustomer extends Notification /** * Get the notification's delivery channels. * - * @param object $notifiable * @return array - * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass */ - public function via(object $notifiable): array + public function via(): array { - // phpcs:enable return ['mail']; } @@ -28,15 +25,12 @@ class DestroyCustomer extends Notification */ public function toMail(object $notifiable): \Illuminate\Notifications\Messages\MailMessage { - /** @var string $firstname */ - $firstname = $notifiable->username; - return (new MailMessage()) ->template('notifications::email-club') ->subject(trans('Supression du compte - :site_name', [ 'site_name' => config('app.name') ])) - ->greeting(trans('Bonjour :name', ['name' => $firstname])) + ->greeting(trans('Bonjour :name', ['name' => $notifiable->username])) ->line(trans( "Votre compte :site_name a été marqué pour suppression, il sera définitivement effacé au bout de 1 semaine. diff --git a/app/Notifications/Customer/DestroyedCustomer.php b/app/Notifications/Customer/DestroyedCustomer.php index 5a4eec54..50e43778 100644 --- a/app/Notifications/Customer/DestroyedCustomer.php +++ b/app/Notifications/Customer/DestroyedCustomer.php @@ -10,26 +10,20 @@ class DestroyedCustomer extends Notification /** * Get the notification's delivery channels. * - * @param object $notifiable * @return array - * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass */ - public function via(object $notifiable): array + public function via(): array { - // phpcs:enable return ['mail']; } /** * Build the mail representation of the notification. * - * @param object $notifiable * @return \Illuminate\Notifications\Messages\MailMessage - * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass */ - public function toMail(object $notifiable): \Illuminate\Notifications\Messages\MailMessage + public function toMail(): \Illuminate\Notifications\Messages\MailMessage { - // phpcs:enable return (new MailMessage()) ->template('notifications::email-club') ->subject(trans('Votre compte est suprimé - :site_name', [ @@ -40,9 +34,7 @@ class DestroyedCustomer extends Notification "Votre compte :site_name a été définitivement supprimé ainsi ques les données associés, si vous aviez effectué une commande elle sera bien sûr toujours honorée. Les commandes associés a votre compte continueront d'exister mais les données de votre compte ont bien été supprimées.", - [ - 'site_name' => config('app.name') - ] + ['site_name' => config('app.name')] )); } } diff --git a/app/Notifications/Customer/ResetPassword.php b/app/Notifications/Customer/ResetPassword.php index dc25a310..14ed5bee 100644 --- a/app/Notifications/Customer/ResetPassword.php +++ b/app/Notifications/Customer/ResetPassword.php @@ -9,7 +9,7 @@ use Illuminate\Support\Facades\URL; class ResetPassword extends NotificationsResetPassword { /** - * ResetPassword + * Reset password. * * @param string $token * @return void diff --git a/app/Notifications/Customer/UpdateEmail.php b/app/Notifications/Customer/UpdateEmail.php index 01ec44a9..31864fdd 100644 --- a/app/Notifications/Customer/UpdateEmail.php +++ b/app/Notifications/Customer/UpdateEmail.php @@ -10,13 +10,10 @@ class UpdateEmail extends Notification /** * Get the notification's delivery channels. * - * @param object $notifiable * @return array - * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass */ - public function via(object $notifiable): array + public function via(): array { - // phpcs:enable return ['mail']; } @@ -28,15 +25,12 @@ class UpdateEmail extends Notification */ public function toMail(object $notifiable): \Illuminate\Notifications\Messages\MailMessage { - /** @var string $firstname */ - $firstname = $notifiable->username; - return (new MailMessage()) ->template('notifications::email-club') ->subject(trans("Mise à jour de l'email - :site_name", [ 'site_name' => config('app.name') ])) - ->greeting(trans('Bonjour :name', ['name' => $firstname])) + ->greeting(trans('Bonjour :name', ['name' => $notifiable->username])) ->line(trans("L'email associé à votre compte :site_name vient d'être modifié.", [ 'site_name' => config('app.name') ])); diff --git a/app/Notifications/Customer/UpdatePassword.php b/app/Notifications/Customer/UpdatePassword.php index 5dc6b402..a3d0d7e2 100644 --- a/app/Notifications/Customer/UpdatePassword.php +++ b/app/Notifications/Customer/UpdatePassword.php @@ -10,13 +10,10 @@ class UpdatePassword extends Notification /** * Get the notification's delivery channels. * - * @param object $notifiable * @return array - * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass */ - public function via(object $notifiable): array + public function via(): array { - // phpcs:enable return ['mail']; } @@ -28,15 +25,12 @@ class UpdatePassword extends Notification */ public function toMail(object $notifiable): \Illuminate\Notifications\Messages\MailMessage { - /** @var string $firstname */ - $firstname = $notifiable->username; - return (new MailMessage()) ->template('notifications::email-club') ->subject(trans("Mise à jour du mot de passe - :site_name", [ 'site_name' => config('app.name') ])) - ->greeting(trans('Bonjour :name', ['name' => $firstname])) + ->greeting(trans('Bonjour :name', ['name' => $notifiable->username])) ->line(trans("Le mot de passe associé à votre compte :site_name vient d'être modifié.", [ 'site_name' => config('app.name') ])); diff --git a/app/Notifications/Customer/VerifyEmail.php b/app/Notifications/Customer/VerifyEmail.php index 870079da..15135f98 100644 --- a/app/Notifications/Customer/VerifyEmail.php +++ b/app/Notifications/Customer/VerifyEmail.php @@ -11,7 +11,7 @@ use Illuminate\Support\Facades\URL; class VerifyEmail extends NotificationsVerifyEmail { /** - * VerifyEmail + * Verify email. * * @return void */ diff --git a/app/Notifications/Shop/NewOrder.php b/app/Notifications/Shop/NewOrder.php new file mode 100644 index 00000000..5663d4c0 --- /dev/null +++ b/app/Notifications/Shop/NewOrder.php @@ -0,0 +1,117 @@ +order = $order; + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via(): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail(): \Illuminate\Notifications\Messages\MailMessage + { + return (new MailMessage()) + ->template('notifications::email-club') + ->subject(trans('Récapitulation de votre commande - :site_name', [ + 'site_name' => config('app.name') + ])) + ->greeting(trans('Merci pour votre achat !')) + ->line(trans("Votre commande a bien été prise en compte et sera traité sous les meilleurs délais.")) + ->line(trans("Vous pouvez retrouver le récapitulif de votre commande en pièce jointe.")) + ->lineIf( + $this->order->payementType->equals(OrderType::check), + trans("Si vous avez choisi un payement par chèque, veuillez l'envoyer à l'adresse suivante :") + ) + ->lineIf( + $this->order->payementType->equals(OrderType::check), + trans("LDC - Les Départ'Aimants Le Gaulois ZI St Laurent - CS 50925 72302 Sablé sur Sarthe") + ) + ->attachData($this->makePdfFile(), 'recapitulatif_commande_le_gaulois.pdf'); + } + + /** + * Create the recap of the order. + * + * @return string + */ + private function makePdfFile(): string + { + return LaravelMpdf::loadHTML( + view('club.pages.shop.template-pdf-order', $this->order->exportToOrderMailData())->render(), + [ + 'mode' => '', + 'format' => 'A4', + 'default_font_size' => '12', + 'default_font' => 'sans-serif', + 'margin_left' => 0, + 'margin_right' => 0, + 'margin_top' => 0, + 'margin_bottom' => 0, + 'margin_header' => 0, + 'margin_footer' => 0, + 'orientation' => 'P', + 'title' => trans('Commande Le Gaulois'), + 'author' => 'Le Gaulois', + 'watermark' => '', + 'show_watermark' => false, + 'show_watermark_image' => false, + 'display_mode' => 'fullpage', + 'watermark_font' => 'sans-serif', + 'watermark_text_alpha' => 0.1, + 'watermark_image_path' => '', + 'watermark_image_alpha' => 0.2, + 'watermark_image_size' => 'D', + 'watermark_image_position' => 'P', + 'custom_font_dir' => base_path('resources/assets/fonts/'), + 'custom_font_data' => [ + 'bouton-base' => [ + 'R' => 'BourtonBase.ttf' + ] + ], + 'auto_language_detection' => false, + 'temp_dir' => rtrim( + ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir(), + DIRECTORY_SEPARATOR + ), + 'pdfa' => false, + 'pdfaauto' => false, + 'use_active_forms' => false, + ] + )->output(); + } +} diff --git a/app/Policies/SellItemPolicy.php b/app/Policies/SellItemPolicy.php new file mode 100644 index 00000000..ed08159a --- /dev/null +++ b/app/Policies/SellItemPolicy.php @@ -0,0 +1,87 @@ +app->environment('production')); @@ -31,12 +31,9 @@ class AppServiceProvider extends ServiceProvider // * HA PROXY FIX App::singleton('clientIp', function () { - if (config('app.env') === 'local') { - return request()->getClientIp(); - } else { - return request()->server('HTTP_X_FORWARDED_FOR') ?? - request()->getClientIp(); - } + return (config('app.env') === 'local') + ? request()->getClientIp() + : request()->server('HTTP_X_FORWARDED_FOR') ?? request()->getClientIp(); }); // * LOCAL FORCE eg: for Carbon @@ -61,7 +58,7 @@ class AppServiceProvider extends ServiceProvider } /** - * Set formatters and payment currency + * Set formatters and payment currency. * * @return void */ diff --git a/app/Providers/CrmApiServiceProvider.php b/app/Providers/CrmApiServiceProvider.php index 20732fce..5c161492 100644 --- a/app/Providers/CrmApiServiceProvider.php +++ b/app/Providers/CrmApiServiceProvider.php @@ -13,7 +13,7 @@ class CrmApiServiceProvider extends ServiceProvider * * @return void */ - public function boot() + public function boot(): void { App::singleton('crmAPI', function () { return new CrmApiV4( diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 546902c0..71a08fab 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -15,7 +15,6 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Session; use Slides\Saml2\Events\SignedIn; -use Slides\Saml2\Events\SignedOut; class EventServiceProvider extends ServiceProvider { @@ -51,28 +50,25 @@ class EventServiceProvider extends ServiceProvider * * @return void */ - public function boot() + public function boot(): void { Event::listen('Slides\Saml2\Events\SignedIn', function (SignedIn $event) { - $messageId = $event->getAuth()->getLastMessageId(); - $fromCache = Cache::get(config('cache.prefix') . "_saml_message_id_" . $messageId); - if ($fromCache) { + if (Cache::get(config('cache.prefix') . "_saml_message_id_" . $messageId)) { logger()->error( 'SAML MESSAGE ID HANDLER', ['error' => 'Message ID : ' . $messageId . " replayed !"] ); abort(403); } else { - Cache::put(config('cache.prefix') . "_saml_message_id_" . $messageId, $messageId, 28800); // Cache for 8h. + Cache::put(config('cache.prefix') . "_saml_message_id_" . $messageId, $messageId, 28800); } $user = $event->getSaml2User(); $email = $user->getNameId(); - - $lUser = User::where('email', '=', $email)->first(); + $lUser = User::query()->where('email', $email)->first(); if (!$lUser) { abort(403); @@ -84,8 +80,7 @@ class EventServiceProvider extends ServiceProvider $lUser->saveQuietly(); }); - //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - Event::listen('Slides\Saml2\Events\SignedOut', function (SignedOut $event) { + Event::listen('Slides\Saml2\Events\SignedOut', function () { Auth::guard('backend')->logout(); Session::save(); }); diff --git a/app/Providers/MesBrApiServiceProvider.php b/app/Providers/MesBrApiServiceProvider.php index 6a9a0564..c0abe219 100644 --- a/app/Providers/MesBrApiServiceProvider.php +++ b/app/Providers/MesBrApiServiceProvider.php @@ -13,7 +13,7 @@ class MesBrApiServiceProvider extends ServiceProvider * * @return void */ - public function boot() + public function boot(): void { App::singleton('mesBrAPI', function () { return new MesBr( diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index c25115c1..23dc8497 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -144,7 +144,7 @@ class RouteServiceProvider extends ServiceProvider * * @return void */ - private function cspReportRoute() + private function cspReportRoute(): void { // * CSP REPORT URI, Please add /csp-report to VerifyCsrfToken $except middleware Route::post('/csp-report', function () { @@ -159,11 +159,11 @@ class RouteServiceProvider extends ServiceProvider })->all(); // * Filter array keys $cspReport = collect(array_intersect_key($cspReport, [ - 'blocked-uri' => '', - 'document-uri' => '', - 'original-policy' => '', - 'referrer' => '', - 'violated-directive' => '' + 'blocked-uri' => '', + 'document-uri' => '', + 'original-policy' => '', + 'referrer' => '', + 'violated-directive' => '', ])); \Illuminate\Support\Facades\Log::warning(sprintf( "CSP REPORT for %s : \n %s", diff --git a/app/Providers/ViewServiceProvider.php b/app/Providers/ViewServiceProvider.php index 6de03d6c..d7818efc 100644 --- a/app/Providers/ViewServiceProvider.php +++ b/app/Providers/ViewServiceProvider.php @@ -96,6 +96,7 @@ class ViewServiceProvider extends ServiceProvider }); // * Inject some data for the bo menu. + // @phpcs:disable Generic.Files.LineLength.TooLong collect([ ['name' => 'customerModelsCount', 'value' => Customer::query()->count()], ['name' => 'firstMeat', 'value' => Meat::query()->first()], @@ -104,62 +105,27 @@ class ViewServiceProvider extends ServiceProvider ['name' => 'mainMenuMeatModels', 'value' => Meat::getPublishedOrdered()], ['name' => 'homeStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::home)], ['name' => 'storyStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::story)], - [ - 'name' => 'recruitmentStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::recruitment) - ], + ['name' => 'recruitmentStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::recruitment)], ['name' => 'sagaStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::saga)], - [ - 'name' => 'recipesStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::recipes) - ], - [ - 'name' => 'recipeStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::recipe) - ], - [ - 'name' => 'productsStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::products) - ], + ['name' => 'recipesStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::recipes)], + ['name' => 'recipeStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::recipe)], + ['name' => 'productsStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::products)], ['name' => 'legalsStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::legals)], - [ - 'name' => 'confidentialityStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::confidentiality) - ], - [ - 'name' => 'sellingStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::selling) - ], - [ - 'name' => 'accessibilityStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::accessibility) - ], - [ - 'name' => 'productStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::product) - ], - [ - 'name' => 'breedingPoultryStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::breedingPoultry) - ], - [ - 'name' => 'breedingJobStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::breedingJob) - ], - [ - 'name' => 'breedingFoodStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::breedingFood) - ], - [ - 'name' => 'breedingHealthStaticPageModel', - 'value' => StaticPage::getCachedStaticPage(StaticPageType::breedingHealth) - ], + ['name' => 'confidentialityStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::confidentiality)], + ['name' => 'sellingStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::selling)], + ['name' => 'accessibilityStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::accessibility)], + ['name' => 'productStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::product)], + ['name' => 'breedingPoultryStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::breedingPoultry)], + ['name' => 'breedingJobStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::breedingJob)], + ['name' => 'breedingFoodStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::breedingFood)], + ['name' => 'breedingHealthStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::breedingHealth)], ['name' => 'newsStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::news)], ])->map(function ($array) { View::share($array['name'], (function () use ($array) { return $array['value'] ?: null; })()); }); + // phpcs:enable } //end if } } diff --git a/app/Rules/Hex.php b/app/Rules/Hex.php index 3ad3239a..199be1a8 100755 --- a/app/Rules/Hex.php +++ b/app/Rules/Hex.php @@ -69,7 +69,7 @@ class Hex implements ValidationRule * * @return string */ - public function message() + public function message(): string { return 'The hex code is not valid'; } diff --git a/app/Rules/IsValidVimeoId.php b/app/Rules/IsValidVimeoId.php index 4a91da6e..6cf03f67 100644 --- a/app/Rules/IsValidVimeoId.php +++ b/app/Rules/IsValidVimeoId.php @@ -31,7 +31,7 @@ class IsValidVimeoId implements ValidationRule } /** - * Validate a value + * Validate a value. * * @param mixed $value * @return boolean diff --git a/app/Rules/IsValidYoutubeId.php b/app/Rules/IsValidYoutubeId.php index 1a410091..b4c3634b 100644 --- a/app/Rules/IsValidYoutubeId.php +++ b/app/Rules/IsValidYoutubeId.php @@ -32,7 +32,7 @@ class IsValidYoutubeId implements ValidationRule } /** - * Validate a value + * Validate a value. * * @param mixed $value * @return boolean diff --git a/app/Traits/Controllers/UpdateModelPublished.php b/app/Traits/Controllers/UpdateModelPublished.php index ae43ef88..7b0c9e33 100644 --- a/app/Traits/Controllers/UpdateModelPublished.php +++ b/app/Traits/Controllers/UpdateModelPublished.php @@ -17,7 +17,7 @@ trait UpdateModelPublished * @param \Illuminate\Database\Eloquent\Model $model * @return \Illuminate\Http\RedirectResponse */ - public function changePublished(Model $model) + public function changePublished(Model $model): \Illuminate\Http\RedirectResponse { $model->update(['published' => !$model->getOriginal('published')]); return redirect()->back()->with('success', trans('crud.messages.publish_status_saved')); diff --git a/app/Traits/Enums/BaseEnum.php b/app/Traits/Enums/BaseEnum.php index f636929c..df82453a 100644 --- a/app/Traits/Enums/BaseEnum.php +++ b/app/Traits/Enums/BaseEnum.php @@ -15,7 +15,7 @@ trait BaseEnum */ public function value(): string|int { - return self::toValues()[$this->name]; + return self::toValues()[$this->name()]; } /** @@ -25,7 +25,7 @@ trait BaseEnum */ public function label(): string { - return self::toLabels()[$this->name]; + return self::toLabels()[$this->name()]; } /** @@ -98,7 +98,7 @@ trait BaseEnum self::setCache('enums', \collect(self::cases()) ->mapWithKeys(function ($enum) use ($definitions) { $baseArray = [ - 'name' => $enum->name, + 'name' => $enum->name, 'label' => $enum->label(), 'value' => $enum->value, ]; diff --git a/app/Traits/Models/IsCachedAsCollection.php b/app/Traits/Models/IsCachedAsCollection.php index 4bbb536c..aec7e468 100644 --- a/app/Traits/Models/IsCachedAsCollection.php +++ b/app/Traits/Models/IsCachedAsCollection.php @@ -36,7 +36,7 @@ trait IsCachedAsCollection } /** - * Remove cached elements + * Remove cached elements. * * @return void */ @@ -63,7 +63,7 @@ trait IsCachedAsCollection } /** - * Change query for cached elements + * Change query for cached elements. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder @@ -74,7 +74,7 @@ trait IsCachedAsCollection } /** - * Change query for cached elements + * Change query for cached elements. * * @param \Illuminate\Support\Collection $results * @return \Illuminate\Support\Collection @@ -85,7 +85,7 @@ trait IsCachedAsCollection } /** - * Get the class basename + * Get the class basename. * * @return string */ @@ -95,7 +95,7 @@ trait IsCachedAsCollection } /** - * Get the class basename + * Get the class basename. * * @return string */ diff --git a/app/Traits/Models/MiniCrudModel.php b/app/Traits/Models/MiniCrudModel.php index e73fefa6..2593f830 100644 --- a/app/Traits/Models/MiniCrudModel.php +++ b/app/Traits/Models/MiniCrudModel.php @@ -27,7 +27,7 @@ trait MiniCrudModel } /** - * Apply function on data typed text while on save event + * Apply function on data typed text while on save event. * * @return void */ @@ -47,7 +47,7 @@ trait MiniCrudModel } /** - * Apply function on data typed text while on update event + * Apply function on data typed text while on update event. * * @return void */ @@ -57,7 +57,7 @@ trait MiniCrudModel } /** - * Apply function on data typed text while on updated event + * Apply function on data typed text while on updated event. * * @return void */ @@ -78,7 +78,7 @@ trait MiniCrudModel } /** - * Apply function on data typed text while on deleted event + * Apply function on data typed text while on deleted event. * * @return void */ @@ -95,7 +95,7 @@ trait MiniCrudModel } /** - * Set a specific prop + * Set a specific prop. * * @param array $data * @return void @@ -110,7 +110,7 @@ trait MiniCrudModel } /** - * Set a specific prop + * Set a specific prop. * * @param array $value * @return void @@ -124,7 +124,7 @@ trait MiniCrudModel } /** - * Set a specific prop + * Set a specific prop. * * @param string|null $data * @return array @@ -135,7 +135,7 @@ trait MiniCrudModel } /** - * Serialize properties + * Serialize properties. * * @param array $data * @return array diff --git a/app/Traits/Requests/HasPicture.php b/app/Traits/Requests/HasPicture.php index aa72cad8..23a6bf34 100644 --- a/app/Traits/Requests/HasPicture.php +++ b/app/Traits/Requests/HasPicture.php @@ -38,7 +38,7 @@ trait HasPicture private $maxHeight = 1800; /** - * Image rules + * Image rules. * * @param string $name * @param integer|null $minWidth @@ -85,7 +85,7 @@ trait HasPicture } /** - * Images rules + * Images rules. * * @param string $name * @param integer|null $minWidth @@ -108,9 +108,9 @@ trait HasPicture bool $nullable = false ): array { $sizes = (object)[ - 'minWidth' => $minWidth ?? $this->minWidth, + 'minWidth' => $minWidth ?? $this->minWidth, 'minHeight' => $minHeight ?? $this->minHeight, - 'maxWidth' => $maxWidth ?? $this->maxWidth, + 'maxWidth' => $maxWidth ?? $this->maxWidth, 'maxHeight' => $maxHeight ?? $this->maxHeight, ]; return [ @@ -126,7 +126,7 @@ trait HasPicture } /** - * Merges products pictures fields with request data + * Merges products pictures fields with request data. * * @param string $name * @return void @@ -153,7 +153,7 @@ trait HasPicture $this->merge([ $name => $changedPicture ]); -// le else if sert a gerer le cas ou l'image de la bannière produit est nulle + // FIXME: patch because of component return. } elseif (is_null($picturePath)) { $this->merge([$name => null]); return; @@ -161,7 +161,7 @@ trait HasPicture } /** - * Merges products pictures fields with request data + * Merges products pictures fields with request data. * * @param string $name * @return void diff --git a/app/View/Components/Fo/Story/HistoricalMilestoneComponent.php b/app/View/Components/Fo/Story/HistoricalMilestoneComponent.php index cb27e729..2019dcd5 100644 --- a/app/View/Components/Fo/Story/HistoricalMilestoneComponent.php +++ b/app/View/Components/Fo/Story/HistoricalMilestoneComponent.php @@ -2,8 +2,6 @@ namespace App\View\Components\Fo\Story; -use Closure; -use Illuminate\Contracts\View\View; use Illuminate\View\Component; use App\Models\StaticPage; use App\Enums\StaticPageType; @@ -23,7 +21,7 @@ class HistoricalMilestoneComponent extends Component public function __construct() { $this->staticPageModel = StaticPage::getCachedStaticPage(StaticPageType::story); - $this->historicalMilestoneModels = HistoricalMilestone::getPublishedOrderedByYear(); + $this->historicalMilestoneModels = HistoricalMilestone::getPublishedOrdered(); } /** @@ -31,10 +29,10 @@ class HistoricalMilestoneComponent extends Component * * @return \Illuminate\Contracts\View\View */ - public function render(): View|Closure|string + public function render(): \Illuminate\Contracts\View\View { return view('components.fo.story.historical-milestone-component', [ - 'staticPageModel' => $this->staticPageModel, + 'staticPageModel' => $this->staticPageModel, 'historicalMilestoneModels' => $this->historicalMilestoneModels ]); } diff --git a/app/View/Components/Fo/Story/WhoWeAre.php b/app/View/Components/Fo/Story/WhoWeAre.php index 7dd717a8..da1c019d 100644 --- a/app/View/Components/Fo/Story/WhoWeAre.php +++ b/app/View/Components/Fo/Story/WhoWeAre.php @@ -2,8 +2,6 @@ namespace App\View\Components\Fo\Story; -use Closure; -use Illuminate\Contracts\View\View; use Illuminate\View\Component; use App\Models\StaticPage; use App\Enums\StaticPageType; @@ -32,7 +30,7 @@ class WhoWeAre extends Component * * @return \Illuminate\Contracts\View\View */ - public function render(): View|Closure|string + public function render(): \Illuminate\Contracts\View\View { return view('components.fo.story.who-we-are', [ 'staticPageModel' => $this->staticPageModel, diff --git a/composer.json b/composer.json index 5a8b7394..87da2e1a 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "snipe/banbuilder": "^2.3", "uwil/admin-secure-laravel": "^3.0", "uwil/k8s-laravel": "^1.4", - "uwil/laravel-common": "^1.0", + "uwil/laravel-common": "^2.1", "uwil/seo-laravel": "^3.0", "verschuur/laravel-robotstxt": "^5.1" }, diff --git a/composer.lock b/composer.lock index 14bb289a..8ed496e7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f7f8ad46520cbcad06c3bc357b29e8be", + "content-hash": "330991754ddc1dffc7b61172bcbc72ea", "packages": [ { "name": "24slides/laravel-saml2", @@ -1057,16 +1057,16 @@ }, { "name": "giggsey/libphonenumber-for-php-lite", - "version": "8.13.46", + "version": "8.13.47", "source": { "type": "git", "url": "https://github.com/giggsey/libphonenumber-for-php-lite.git", - "reference": "d24dcb40b2d370a128fb3eee472c33752f7d6e66" + "reference": "4c8616d0f358ed5722883761180eccaa2ff5f768" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/d24dcb40b2d370a128fb3eee472c33752f7d6e66", - "reference": "d24dcb40b2d370a128fb3eee472c33752f7d6e66", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/4c8616d0f358ed5722883761180eccaa2ff5f768", + "reference": "4c8616d0f358ed5722883761180eccaa2ff5f768", "shasum": "" }, "require": { @@ -1136,7 +1136,7 @@ "issues": "https://github.com/giggsey/libphonenumber-for-php-lite/issues", "source": "https://github.com/giggsey/libphonenumber-for-php-lite" }, - "time": "2024-09-25T08:51:25+00:00" + "time": "2024-10-08T08:31:05+00:00" }, { "name": "graham-campbell/result-type", @@ -1611,6 +1611,150 @@ ], "time": "2023-12-03T19:50:20+00:00" }, + { + "name": "intervention/gif", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/Intervention/gif.git", + "reference": "42c131a31b93c440ad49061b599fa218f06f93be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/gif/zipball/42c131a31b93c440ad49061b599fa218f06f93be", + "reference": "42c131a31b93c440ad49061b599fa218f06f93be", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "phpstan/phpstan": "^1", + "phpunit/phpunit": "^10.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Gif\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "Native PHP GIF Encoder/Decoder", + "homepage": "https://github.com/intervention/gif", + "keywords": [ + "animation", + "gd", + "gif", + "image" + ], + "support": { + "issues": "https://github.com/Intervention/gif/issues", + "source": "https://github.com/Intervention/gif/tree/4.2.0" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2024-09-20T13:35:02+00:00" + }, + { + "name": "intervention/image", + "version": "3.8.0", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image.git", + "reference": "1786ad5e1789050939d73cd195de4b8eaeeb34ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image/zipball/1786ad5e1789050939d73cd195de4b8eaeeb34ed", + "reference": "1786ad5e1789050939d73cd195de4b8eaeeb34ed", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "intervention/gif": "^4.1", + "php": "^8.1" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^1", + "phpunit/phpunit": "^10.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "suggest": { + "ext-exif": "Recommended to be able to read EXIF data properly." + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "PHP image manipulation", + "homepage": "https://image.intervention.io/", + "keywords": [ + "gd", + "image", + "imagick", + "resize", + "thumbnail", + "watermark" + ], + "support": { + "issues": "https://github.com/Intervention/image/issues", + "source": "https://github.com/Intervention/image/tree/3.8.0" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2024-08-16T14:57:26+00:00" + }, { "name": "jean85/pretty-package-versions", "version": "2.0.6", @@ -1670,6 +1814,68 @@ }, "time": "2024-03-08T09:58:59+00:00" }, + { + "name": "kwaadpepper/image-resizer", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/Kwaadpepper/image-resizer.git", + "reference": "fac49db8819135a64c62e5f8143288cb59c1cad1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Kwaadpepper/image-resizer/zipball/fac49db8819135a64c62e5f8143288cb59c1cad1", + "reference": "fac49db8819135a64c62e5f8143288cb59c1cad1", + "shasum": "" + }, + "require": { + "illuminate/support": "~11", + "intervention/image": "^3.6", + "laravel/framework": "~11", + "php": "^8.2" + }, + "require-dev": { + "larastan/larastan": "^2.0", + "orchestra/testbench": "^9.0", + "slevomat/coding-standard": "^8.11" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Kwaadpepper\\ImageResizer\\ImageResizerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Kwaadpepper\\ImageResizer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jérémy Munsch", + "email": "github@jeremydev.ovh", + "homepage": "https://jeremydev.ovh" + } + ], + "description": "Resize images on the fly and cache them", + "homepage": "https://github.com/kwaadpepper/image-resizer", + "keywords": [ + "images", + "laravel", + "resize" + ], + "support": { + "issues": "https://github.com/Kwaadpepper/image-resizer/issues", + "source": "https://github.com/Kwaadpepper/image-resizer/tree/3.0.2" + }, + "time": "2024-06-21T17:48:22+00:00" + }, { "name": "laminas/laminas-diactoros", "version": "3.4.0", @@ -2637,16 +2843,16 @@ }, { "name": "league/flysystem", - "version": "3.29.0", + "version": "3.29.1", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "0adc0d9a51852e170e0028a60bd271726626d3f0" + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/0adc0d9a51852e170e0028a60bd271726626d3f0", - "reference": "0adc0d9a51852e170e0028a60bd271726626d3f0", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", "shasum": "" }, "require": { @@ -2714,9 +2920,9 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.29.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" }, - "time": "2024-09-29T11:59:11+00:00" + "time": "2024-10-08T08:58:34+00:00" }, { "name": "league/flysystem-local", @@ -7980,28 +8186,29 @@ }, { "name": "uwil/laravel-common", - "version": "1.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "git@lab.s-web.fr:ldc-web/projets/laravel-common.git", - "reference": "399591bd611a4c1b4fc9e308eba87208234872b8" + "reference": "eaa9de3a92d36230a00fd6b65c0add4fc49b88df" }, "dist": { "type": "zip", - "url": "https://lab.s-web.fr/api/v4/projects/215/packages/composer/archives/uwil/laravel-common.zip?sha=399591bd611a4c1b4fc9e308eba87208234872b8", - "reference": "399591bd611a4c1b4fc9e308eba87208234872b8", + "url": "https://lab.s-web.fr/api/v4/projects/215/packages/composer/archives/uwil/laravel-common.zip?sha=eaa9de3a92d36230a00fd6b65c0add4fc49b88df", + "reference": "eaa9de3a92d36230a00fd6b65c0add4fc49b88df", "shasum": "" }, "require": { - "illuminate/support": "~10|~11", - "laravel/framework": "~10|~11", - "php": "~8" + "illuminate/support": "~11", + "kwaadpepper/image-resizer": "^3.0", + "laravel/framework": "~11", + "php": "~8.3" }, "require-dev": { "larastan/larastan": "^2.9", - "orchestra/testbench": "^8.5", - "phpunit/php-code-coverage": "^9.2", - "phpunit/phpunit": "^9.6", + "orchestra/testbench": "^9.0", + "phpunit/php-code-coverage": "^11.0", + "phpunit/phpunit": "^11.0", "slevomat/coding-standard": "^8.3", "squizlabs/php_codesniffer": "^3.7" }, @@ -8014,6 +8221,9 @@ } }, "autoload": { + "files": [ + "src/Helpers/methods.php" + ], "psr-4": { "Uwil\\Laravel\\Common\\": "src/" } diff --git a/config/arcadia.php b/config/arcadia.php new file mode 100644 index 00000000..3dd2c4fc --- /dev/null +++ b/config/arcadia.php @@ -0,0 +1,5 @@ + env('MAIL_MKT_JEUX') +]; diff --git a/config/filesystems.php b/config/filesystems.php index 89920a55..062d453a 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -32,37 +32,41 @@ return [ 'local' => [ 'driver' => 'local', - 'root' => storage_path('app'), - 'throw' => false, + 'root' => storage_path('app'), + 'throw' => false, ], 'public' => [ - 'driver' => 'local', - 'root' => storage_path('app/public'), - 'url' => env('APP_URL') . '/storage', + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL') . '/storage', 'visibility' => 'public', - 'throw' => false, + 'throw' => false, ], 'private' => [ - 'driver' => 'local', - 'root' => storage_path('app/private'), + 'driver' => 'local', + 'root' => storage_path('app/private'), 'visibility' => 'private', - 'throw' => false, + 'throw' => false, ], 's3' => [ - 'driver' => 's3', - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION'), - 'bucket' => env('AWS_BUCKET'), - 'url' => env('AWS_URL'), - 'endpoint' => env('AWS_ENDPOINT'), + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), - 'throw' => false, + 'throw' => false, ], + 'arcadia' => [ + 'driver' => 'local', + 'root' => storage_path('arcadia'), + ], ], /* diff --git a/config/mercanet.php b/config/mercanet.php new file mode 100644 index 00000000..9864fd26 --- /dev/null +++ b/config/mercanet.php @@ -0,0 +1,8 @@ + env('MERCANET_SECRET'), + 'merchantId' => env('MERCANET_MERCHAND_ID'), + // Ex: DEVELOPMENT, PRODUCTION. + 'env' => env('MERCANET_ENV', 'DEVELOPMENT'), +]; diff --git a/config/robots-txt.php b/config/robots-txt.php index 6d4614ab..e43f1dbe 100644 --- a/config/robots-txt.php +++ b/config/robots-txt.php @@ -1,4 +1,5 @@ [ 'production' => [ diff --git a/database/factories/NewsClubFactory.php b/database/factories/NewsClubFactory.php index 6cf68020..c8964cc7 100644 --- a/database/factories/NewsClubFactory.php +++ b/database/factories/NewsClubFactory.php @@ -2,7 +2,7 @@ namespace Database\Factories; -use App\Enums\Club\NewsClubTypeEnum; +use App\Enums\News\NewsClubTypeEnum; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; diff --git a/database/factories/SurveysAnswerFactory.php b/database/factories/SurveysAnswerFactory.php index 4311def7..fd7b524f 100644 --- a/database/factories/SurveysAnswerFactory.php +++ b/database/factories/SurveysAnswerFactory.php @@ -2,7 +2,7 @@ namespace Database\Factories; -use App\Enums\Club\NewsClubTypeEnum; +use App\Enums\News\NewsClubTypeEnum; use App\Models\NewsClub; use Illuminate\Database\Eloquent\Factories\Factory; diff --git a/database/migrations/2024_09_19_093926_alter_cookingtime_recipes_table.php b/database/migrations/2024_09_19_093926_alter_cookingtime_recipes_table.php index c1108c28..222a187f 100644 --- a/database/migrations/2024_09_19_093926_alter_cookingtime_recipes_table.php +++ b/database/migrations/2024_09_19_093926_alter_cookingtime_recipes_table.php @@ -22,7 +22,7 @@ return new class extends Migration public function down(): void { Schema::table('recipes', function (Blueprint $table) { - $table->unsignedSmallInteger('cooking_time')->nullable(false); + $table->unsignedSmallInteger('cooking_time')->nullable(false)->change(); }); } }; diff --git a/database/migrations/2024_12_02_141232_create_departaimants_table.php b/database/migrations/2024_12_02_141232_create_departaimants_table.php index 7ee5431b..06f3d004 100644 --- a/database/migrations/2024_12_02_141232_create_departaimants_table.php +++ b/database/migrations/2024_12_02_141232_create_departaimants_table.php @@ -16,9 +16,7 @@ class CreateDepartaimantsTable extends Migration { Schema::create('departaimants', function (Blueprint $table) { $table->id(); - $table->smallInteger('type')->unsigned(); - /** * bigInt = 8 octets (64bits) * Donc à raison de 2 colonnes par départements @@ -31,12 +29,9 @@ class CreateDepartaimantsTable extends Migration $table->bigInteger('col2')->unsigned()->default(0); $table->bigInteger('col3')->unsigned()->default(0); $table->bigInteger('col4')->unsigned()->default(0); - $table->foreignId('customer_id'); $table->foreign('customer_id')->references('id')->on('customers')->onDelete('cascade'); - $table->timestamps(); - $table->unique(['type', 'customer_id']); }); } diff --git a/database/migrations/2024_12_09_112347_create_messages_table.php b/database/migrations/2024_12_09_112347_create_messages_table.php index 5e7ffd51..10cadf4f 100644 --- a/database/migrations/2024_12_09_112347_create_messages_table.php +++ b/database/migrations/2024_12_09_112347_create_messages_table.php @@ -16,16 +16,13 @@ class CreateMessagesTable extends Migration { Schema::create('messages', function (Blueprint $table) { $table->id(); - $table->string('message', 255); $table->boolean('hide_from')->default(false); $table->boolean('hide_to')->default(false); - $table->foreignId('from')->nullable(); $table->foreign('from')->references('id')->on('customers')->cascadeOnDelete(); $table->foreignId('to')->nullable(); $table->foreign('to')->references('id')->on('customers')->cascadeOnDelete(); - $table->timestamps(); }); } diff --git a/database/migrations/2024_12_10_171912_create_block_lists_table.php b/database/migrations/2024_12_10_171912_create_block_lists_table.php index 16a8fa2b..fdcc8d3a 100644 --- a/database/migrations/2024_12_10_171912_create_block_lists_table.php +++ b/database/migrations/2024_12_10_171912_create_block_lists_table.php @@ -16,12 +16,10 @@ class CreateBlockListsTable extends Migration { Schema::create('block_lists', function (Blueprint $table) { $table->id(); - $table->foreignId('customer_id')->nullable(); $table->foreign('customer_id')->references('id')->on('customers')->cascadeOnDelete(); $table->foreignId('blocked_id')->nullable(); $table->foreign('blocked_id')->references('id')->on('customers')->cascadeOnDelete(); - $table->timestamps(); }); } diff --git a/database/migrations/2024_21_27_123905_create_sell_items_table.php b/database/migrations/2024_21_27_123905_create_sell_items_table.php new file mode 100644 index 00000000..e18748e4 --- /dev/null +++ b/database/migrations/2024_21_27_123905_create_sell_items_table.php @@ -0,0 +1,39 @@ +id()->comment('Sell item id.'); + $table->string('name')->comment('Sell item name.'); + $table->string('picture')->comment('Sell item picture path.'); + $table->string('picture_alt')->comment('Picture alt attribute.'); + $table->string('picture_title')->comment('Picture title attribute.'); + $table->float('price')->comment('Sell item price.'); + $table->smallInteger('arcadia_weight')->default(0)->comment('Sell item arcadia weight.'); + $table->integer('arcadia_id')->unique()->comment('Sell item arcadia id.'); + $table->integer('order')->comment('Sell item order'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::dropIfExists('sell_items'); + } +}; diff --git a/database/migrations/2024_21_27_123906_create_orders_table.php b/database/migrations/2024_21_27_123906_create_orders_table.php new file mode 100644 index 00000000..46a7f0d3 --- /dev/null +++ b/database/migrations/2024_21_27_123906_create_orders_table.php @@ -0,0 +1,45 @@ +id(); + $table->uuid('ref')->unique()->comment('Order reference.'); + $table->string('firstname')->comment('Order firstname author.'); + $table->string('lastname')->comment('Order lastname author.'); + $table->string('address')->comment('Order address author.'); + $table->string('postcode')->comment('Order postcode author.'); + $table->string('phone')->nullable()->comment('Order phone author.'); + $table->string('email')->comment('Order email author.'); + $table->string('city')->comment('Order city author.'); + $table->tinyInteger('civility')->comment('Order civility author.'); + $table->tinyInteger('status')->comment('Order status.'); + $table->tinyInteger('payementType')->comment('Order payement type.'); + $table->boolean('confirmMailSent')->default(false)->comment('Order sent mail confirm.'); + $table->foreignId('customer_id')->comment('Customer associated to the order.') + ->constrained('customers')->onUpdate('cascade')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::dropIfExists('orders'); + } +}; diff --git a/database/migrations/2024_21_27_123907_create_order_sell_item_table.php b/database/migrations/2024_21_27_123907_create_order_sell_item_table.php new file mode 100644 index 00000000..dd261804 --- /dev/null +++ b/database/migrations/2024_21_27_123907_create_order_sell_item_table.php @@ -0,0 +1,32 @@ +id(); + $table->smallInteger('quantity')->comment('Order sell items quantity.'); + $table->foreignId('order_id')->comment('Order associated to the sell item.') + ->constrained('orders')->onUpdate('cascade')->onDelete('cascade'); + $table->foreignId('sell_item_id')->comment('Sell item associated to the order.') + ->constrained('sell_items')->onUpdate('cascade')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('order_sell_item'); + } +}; diff --git a/database/migrations/2024_21_27_123908_create_delivery_addresses_table.php b/database/migrations/2024_21_27_123908_create_delivery_addresses_table.php new file mode 100644 index 00000000..84e08d9b --- /dev/null +++ b/database/migrations/2024_21_27_123908_create_delivery_addresses_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('firstname')->comment('Delivery address author firstname.'); + $table->string('lastname')->comment('Delivery address author lastname.'); + $table->string('address')->comment('Delivery address address.'); + $table->string('postcode')->comment('Delivery address postcode.'); + $table->string('phone')->nullable()->comment('Delivery address author phone.'); + $table->string('city')->comment('Delivery address city.'); + $table->tinyInteger('civility')->comment('Delivery address author civility.'); + $table->foreignId('order_id')->comment('Order associated to the delivery address.') + ->constrained('orders')->onUpdate('cascade')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('delivery_addresses'); + } +}; diff --git a/database/migrations/2024_21_27_123910_add_type_sell_items_table.php b/database/migrations/2024_21_27_123910_add_type_sell_items_table.php new file mode 100644 index 00000000..4623c47b --- /dev/null +++ b/database/migrations/2024_21_27_123910_add_type_sell_items_table.php @@ -0,0 +1,33 @@ +tinyInteger('type')->comment('Sell item type.')->default(SellItemsType::paper)->after('order'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('sell_items', function (Blueprint $table) { + $table->dropColumn('type'); + }); + } +}; diff --git a/package-lock.json b/package-lock.json index f702ed30..013e41cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2844,9 +2844,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001610", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001610.tgz", - "integrity": "sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA==", + "version": "1.0.30001664", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz", + "integrity": "sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==", "dev": true, "funding": [ { diff --git a/resources/assets/documents/club/conditions_generales_de_vente.pdf b/resources/assets/documents/club/conditions_generales_de_vente.pdf new file mode 100644 index 00000000..5bdc244b Binary files /dev/null and b/resources/assets/documents/club/conditions_generales_de_vente.pdf differ diff --git a/resources/assets/fonts/BourtonBase.ttf b/resources/assets/fonts/BourtonBase.ttf new file mode 100644 index 00000000..2e7d8722 Binary files /dev/null and b/resources/assets/fonts/BourtonBase.ttf differ diff --git a/resources/assets/images/club/shop/order.jpg b/resources/assets/images/club/shop/order.jpg new file mode 100644 index 00000000..e74e9938 Binary files /dev/null and b/resources/assets/images/club/shop/order.jpg differ diff --git a/resources/assets/images/club/shop/padlock.png b/resources/assets/images/club/shop/padlock.png new file mode 100644 index 00000000..5e8819dd Binary files /dev/null and b/resources/assets/images/club/shop/padlock.png differ diff --git a/resources/js/vendor/ckeditor/package.json b/resources/js/vendor/ckeditor/package.json index 6fdc992a..351d2d14 100644 --- a/resources/js/vendor/ckeditor/package.json +++ b/resources/js/vendor/ckeditor/package.json @@ -1,6 +1,5 @@ { "name": "ckeditor5-calliope-build", - "author": "Jeremy Munsch", "description": "Build spécial Calliope avec adaptateur Laravel File Manager", "version": "0.2.0", "license": "SEE LICENSE IN LICENSE.md", diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php index 6db4982c..b4f94842 100644 --- a/resources/lang/en/auth.php +++ b/resources/lang/en/auth.php @@ -3,7 +3,8 @@ declare(strict_types=1); return [ - 'failed' => 'These credentials do not match our records.', - 'password' => 'The password is incorrect.', - 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + 'failed' => 'These credentials do not match our records.', + 'password' => 'The password is incorrect.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + 'unpublished_user' => 'Your account has been deactivated, please contact a site administrator.', ]; diff --git a/resources/lang/en/crud.php b/resources/lang/en/crud.php index 06160129..70d7e022 100644 --- a/resources/lang/en/crud.php +++ b/resources/lang/en/crud.php @@ -78,6 +78,7 @@ return [ 'other' => [ 'up' => 'Change the order of appearance upwards', 'down' => 'Change the order of appearance downwards', + 'highlight' => 'Highlight', 'no_model_found' => 'No :model found|No :model found', 'publish' => 'Actually Published', 'unpublish' => 'Actually Unpublished', diff --git a/resources/lang/en/models.php b/resources/lang/en/models.php index e50c3762..78c754dc 100644 --- a/resources/lang/en/models.php +++ b/resources/lang/en/models.php @@ -29,6 +29,7 @@ return [ 'offer' => 'offer', 'testimonial' => 'testimonial', 'news' => 'news', + 'news_breeding' => 'news breeding|news breeding', 'news_corporate' => 'news of the compagny|news of the compagny', 'news_category' => 'category of the news|categories of the news', 'news_club' => 'news of the club|news of the club', @@ -37,5 +38,6 @@ return [ 'customer' => 'club user', 'breeder_map_component' => '\'Breeders map\' component', 'discount' => 'discount', + 'sell_item' => 'item on sale|items on sale', ] ]; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index a8a5d10d..fe235278 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -232,6 +232,9 @@ return [ ], 'activated' => 'activated', 'advice' => 'advice', + 'associated_page' => 'associated page|associated pages', + 'arcadiaId' => 'arcadia id', + 'arcadiaWeight' => 'arcadia weight', 'attribute_alt' => 'alt attribute', 'attribute_title' => 'title attribute', 'background' => 'background', @@ -249,6 +252,7 @@ return [ 'claim' => 'claim', 'claim_title' => 'claims title', 'class' => 'class', + 'code' => 'code', 'color_block' => 'color block', 'color' => 'color', 'contract' => 'contract', @@ -279,6 +283,7 @@ return [ 'picto' => 'pictogram', 'preparation_time' => 'preparation time (in minutes)', 'preparation' => 'preparation', + 'price' => 'price', 'product_id' => 'product ID', 'publication' => 'publication', 'publish_at' => 'publish at', @@ -309,7 +314,16 @@ return [ 'main_text' => 'main text', 'picto_text' => 'text of the pictogram', ], - 'code' => 'code', - 'used' => 'used', + 'nutritionnal_values' => [ + 'label' => 'nutritionnal values', + 'energy' => 'energy', + 'proteins' => 'proteins', + 'carbohydrates' => 'carbohydrates', + 'sugar' => 'sugar', + 'fat' => 'fat', + 'saturated_fatty_acids' => 'saturated fatty acids', + 'dietary_fiber' => 'dietary fiber', + 'salt' => 'salt', + ] ], ]; diff --git a/resources/lang/fr/auth.php b/resources/lang/fr/auth.php index a22cd3f7..96d63833 100644 --- a/resources/lang/fr/auth.php +++ b/resources/lang/fr/auth.php @@ -3,7 +3,8 @@ declare(strict_types=1); return [ - 'failed' => 'Ces identifiants ne correspondent pas à nos enregistrements.', - 'password' => 'Le mot de passe est incorrect', - 'throttle' => 'Tentatives de connexion trop nombreuses. Veuillez essayer de nouveau dans :seconds secondes.', + 'failed' => 'Ces identifiants ne correspondent pas à nos enregistrements.', + 'password' => 'Le mot de passe est incorrect', + 'throttle' => 'Tentatives de connexion trop nombreuses. Veuillez essayer de nouveau dans :seconds secondes.', + 'unpublished_user' => 'Votre compte à été désactivé, veuillez contactez un administrateur du site.', ]; diff --git a/resources/lang/fr/crud.php b/resources/lang/fr/crud.php index 8f39fa5f..32f37a6b 100644 --- a/resources/lang/fr/crud.php +++ b/resources/lang/fr/crud.php @@ -89,6 +89,7 @@ return [ 'other' => [ 'up' => 'Modifier l\'ordre d\'apparition vers le haut', 'down' => 'Modifier l\'ordre d\'apparition vers le bas', + 'highlight' => 'mise en avant', 'no_model_found' => 'Aucun :model trouvé|Aucune :model trouvée', 'publish' => 'Actuellement Publié', 'unpublish' => 'Actuellement Dépublié', diff --git a/resources/lang/fr/models.php b/resources/lang/fr/models.php index deec2180..572afecf 100644 --- a/resources/lang/fr/models.php +++ b/resources/lang/fr/models.php @@ -38,5 +38,6 @@ return [ 'customer' => 'utilisateur du club', 'breeder_map_component' => 'composant \'Carte des éleveurs\'', 'discount' => 'réduction', + 'sell_item' => 'objet en vente|objets en vente', ] ]; diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index 2f1888f2..df659f59 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -235,6 +235,8 @@ return [ 'activated' => 'activé', 'advice' => 'conseil', 'associated_page' => 'page associée|pages associées', + 'arcadiaId' => 'arcadia id', + 'arcadiaWeight' => 'arcadia poids', 'attribute_alt' => 'attribut alt', 'attribute_title' => 'attribut titre', 'background' => 'image de fond', @@ -287,6 +289,7 @@ return [ 'picto' => 'pictogramme', 'preparation_time' => 'temps de préparation (en minutes)', 'preparation' => 'préparation', + 'price' => 'prix', 'product_id' => 'ID du produit', 'publication' => 'publication', 'publish_at' => 'date de publication', @@ -324,21 +327,20 @@ return [ 'attachment' => 'pièce jointe', 'town' => 'ville', 'civility' => 'civilité', - - 'breeder_map_component' => [ 'main_text' => 'texte principal', 'picto_text' => 'texte du pictogramme', ], 'nutritionnal_values' => [ - 'energy' => 'énergie', - 'proteins' => 'protéines', - 'carbohydrates' => 'glucides', - 'sugar' => 'dont sucres', - 'fat' => 'matières grasses', + 'label' => 'valeurs nutritionnelles', + 'energy' => 'énergie', + 'proteins' => 'protéines', + 'carbohydrates' => 'glucides', + 'sugar' => 'dont sucres', + 'fat' => 'matières grasses', 'saturated_fatty_acids' => 'dont Acides Gras Saturés', - 'dietary_fiber' => 'fibres alimentaires', - 'salt' => 'sel', + 'dietary_fiber' => 'fibres alimentaires', + 'salt' => 'sel', ] ], ]; diff --git a/resources/sass/club/partials/_card-club-home.scss b/resources/sass/club/partials/_card-club-home.scss index a14d0e49..e8325bc3 100644 --- a/resources/sass/club/partials/_card-club-home.scss +++ b/resources/sass/club/partials/_card-club-home.scss @@ -37,7 +37,7 @@ .promo-badge { position: absolute; top: -1rem; - right: .5rem; + right: 0; padding: .2rem .3rem; background-color: $color-hex-red; color: $color-hex-light; diff --git a/resources/sass/fo/base/_layout.scss b/resources/sass/fo/base/_layout.scss index 1465bf18..ec1c1d18 100644 --- a/resources/sass/fo/base/_layout.scss +++ b/resources/sass/fo/base/_layout.scss @@ -420,8 +420,10 @@ body { } } + // FIXME: uncomment next lines when club.games & club.gifts are done .svg-little-chicken { - top: -40%; + // top: -40%; + top: 15%; left: 5%; width: 1.3rem; @@ -432,7 +434,8 @@ body { } &-puzzle { - right: 8.8rem; + // right: 8.8rem; + right: 3.8rem; bottom: 0.2rem; } diff --git a/resources/sass/fo/pages/recipes.scss b/resources/sass/fo/pages/recipes.scss index a4856641..d6f249f7 100644 --- a/resources/sass/fo/pages/recipes.scss +++ b/resources/sass/fo/pages/recipes.scss @@ -35,7 +35,9 @@ margin-top: -1rem; @include media-breakpoint-up(md) { - margin-top: -2rem; + position: relative; + left: 20%; + margin-top: -5rem; } .card-header { diff --git a/resources/ts/bo/utilities/fontawesome.ts b/resources/ts/bo/utilities/fontawesome.ts index 7c4287dd..a0333d59 100644 --- a/resources/ts/bo/utilities/fontawesome.ts +++ b/resources/ts/bo/utilities/fontawesome.ts @@ -18,6 +18,7 @@ import { faBackspace, faBan, faBars, + faBasketShopping, faBriefcase, faBuildingWheat, faChartSimple, @@ -171,5 +172,6 @@ library.add(faPeopleGroup); library.add(faChartSimple); library.add(faComments); library.add(faRug); +library.add(faBasketShopping); dom.watch(); diff --git a/resources/ts/club/components/InputBirthDate.vue b/resources/ts/club/components/InputBirthDate.vue index 655d2333..bae0dc7e 100644 --- a/resources/ts/club/components/InputBirthDate.vue +++ b/resources/ts/club/components/InputBirthDate.vue @@ -45,7 +45,6 @@ export default defineComponent({ cached_value: null, }; }, - computed: {}, mounted() { const json = String(this.$attrs.json ?? "{}"), data = JSON.parse(json); diff --git a/resources/ts/club/components/club/boutique/choose.vue.save b/resources/ts/club/components/club/boutique/choose.vue.save deleted file mode 100644 index d11f135e..00000000 --- a/resources/ts/club/components/club/boutique/choose.vue.save +++ /dev/null @@ -1,140 +0,0 @@ - - - diff --git a/resources/ts/club/components/club/boutique/recap.vue.save b/resources/ts/club/components/club/boutique/recap.vue.save deleted file mode 100644 index 172a2b6e..00000000 --- a/resources/ts/club/components/club/boutique/recap.vue.save +++ /dev/null @@ -1,369 +0,0 @@ - - - diff --git a/resources/ts/club/components/club/boutique/shop.vue.save b/resources/ts/club/components/club/boutique/shop.vue.save deleted file mode 100644 index f601e827..00000000 --- a/resources/ts/club/components/club/boutique/shop.vue.save +++ /dev/null @@ -1,27 +0,0 @@ - - - diff --git a/resources/ts/club/components/club/shop/ChooseComponent.vue b/resources/ts/club/components/club/shop/ChooseComponent.vue new file mode 100644 index 00000000..b0dcd71b --- /dev/null +++ b/resources/ts/club/components/club/shop/ChooseComponent.vue @@ -0,0 +1,270 @@ + + + + + diff --git a/resources/ts/club/components/club/shop/RecapComponent.vue b/resources/ts/club/components/club/shop/RecapComponent.vue new file mode 100644 index 00000000..1b6e6b31 --- /dev/null +++ b/resources/ts/club/components/club/shop/RecapComponent.vue @@ -0,0 +1,622 @@ + + + + + + diff --git a/resources/ts/club/components/club/shop/ShopRouterComponent.vue b/resources/ts/club/components/club/shop/ShopRouterComponent.vue new file mode 100644 index 00000000..eb255857 --- /dev/null +++ b/resources/ts/club/components/club/shop/ShopRouterComponent.vue @@ -0,0 +1,161 @@ + + + diff --git a/resources/ts/club/utilities/fontawesome.ts b/resources/ts/club/utilities/fontawesome.ts index 0b286f88..596c4f91 100644 --- a/resources/ts/club/utilities/fontawesome.ts +++ b/resources/ts/club/utilities/fontawesome.ts @@ -3,6 +3,7 @@ import { faSquare, faSquareCheck } from "@fortawesome/free-regular-svg-icons"; import { faArrowRightArrowLeft, faBan, + faBasketShopping, faChevronLeft, faChevronRight, faCircleCheck, @@ -15,6 +16,9 @@ import { faEyeSlash, faLocationArrow, faMagnifyingGlass, + faMinus, + faPlus, + faSpinner, faTriangleExclamation, faXmark } from "@fortawesome/free-solid-svg-icons"; @@ -48,4 +52,10 @@ library.add(faArrowRightArrowLeft); library.add(faEllipsisVertical); library.add(faLocationArrow); +// Shop +library.add(faPlus); +library.add(faMinus); +library.add(faBasketShopping); +library.add(faSpinner); + dom.watch(); diff --git a/resources/ts/index.d.ts b/resources/ts/index.d.ts index fe122586..4ddfbcaa 100644 --- a/resources/ts/index.d.ts +++ b/resources/ts/index.d.ts @@ -97,6 +97,31 @@ declare global { swiper: Swiper; }; + type ClubShop = { + ref: number|undefined; + sellItems: ClubShopItem[]; + sellItemsType: { label: string, value: number, color: string}[]; + payementMethods: { label: string, value: number}[]; + civility: { label: string, value: number}[]; + user: LaravelModel; + cart: { + item: ClubShopItem, + count: number; + }[]; + total: number; + }; + + type ClubShopItem = { + id: number; + name: string; + htmlName: string; + picture: string; + picture_alt: string; + picture_title: string; + price: number; + type: number; + }; + type ValueOf = T[keyof T]; type LaravelValidationErrors = Record; @@ -107,6 +132,7 @@ declare global { CookieNotice: typeof CookieNotice; axios: AxiosStatic; Swal: Function; + shop: ClubShop; vueDebug: boolean | undefined; __SLIDERS: Map, __SYSTEM: { diff --git a/resources/ts/modules/asset.ts b/resources/ts/modules/asset.ts index 7db4d6ee..f4e7ea82 100644 --- a/resources/ts/modules/asset.ts +++ b/resources/ts/modules/asset.ts @@ -1,7 +1,7 @@ export default { methods: { asset(path: string) { - return `${window.__SYSTEM._asset[path] ?? path}`; + return `${window.__SYSTEM._asset}${path}`; }, }, }; diff --git a/resources/views/bo/layouts/sidemenu.blade.php b/resources/views/bo/layouts/sidemenu.blade.php index d744488c..2b2e1ef6 100644 --- a/resources/views/bo/layouts/sidemenu.blade.php +++ b/resources/views/bo/layouts/sidemenu.blade.php @@ -977,19 +977,21 @@ 'fw-bold active' => request()->routeIs('bo.news_clubs.*') || request()->routeIs('bo.surveys_answers.*') || - request()->routeIs('bo.discounts.*'), + request()->routeIs('bo.discounts.*') || + request()->routeIs('bo.sell_items.*'), ])> request()->routeIs('bo.news_clubs.*') || request()->routeIs('bo.surveys_answers.*') || - request()->routeIs('bo.discounts.*'), + request()->routeIs('bo.discounts.*') || + request()->routeIs('bo.sell_items.*'), ])> {{ __('Club') }} @include('club.layouts.main-menu.offcanvas-menu', ['id' => 'offcanvas-main-menu']) - @include('club.layouts.main-menu.offcanvas-search', ['id' => 'offcanvas-search-menu']) + {{-- FIXME:uncomment when search functionnality is ready --}} + {{-- @include('club.layouts.main-menu.offcanvas-search', ['id' => 'offcanvas-search-menu']) --}} @endauth diff --git a/resources/views/club/layouts/main-menu/offcanvas-menu.blade.php b/resources/views/club/layouts/main-menu/offcanvas-menu.blade.php index 96afe7f8..501e5707 100644 --- a/resources/views/club/layouts/main-menu/offcanvas-menu.blade.php +++ b/resources/views/club/layouts/main-menu/offcanvas-menu.blade.php @@ -7,10 +7,12 @@