diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml new file mode 100644 index 0000000..225a807 --- /dev/null +++ b/.github/workflows/phpstan.yml @@ -0,0 +1,39 @@ +name: Tests PHPStan in environments + +on: [pull_request] + +jobs: + php82-laravel-latest-phpstan-postgres: + runs-on: ubuntu-latest + container: + image: escolalms/php:8.2 + + services: + postgres: + image: postgres:12 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test + TZ: Europe/Warsaw + ports: + - 5432:5432 + + steps: + - name: Instantiate package + uses: actions/checkout@v2 + + - name: Setup environment + run: cp env/postgres/* . + + - name: Update composer + run: COMPOSER_ROOT_VERSION=0.9.9 composer update + + - name: Clear config + run: vendor/bin/testbench config:clear + + - name: Publish things + run: vendor/bin/testbench migrate:fresh + + - name: Run tests + run: vendor/bin/phpstan analyse diff --git a/composer.json b/composer.json index 933b515..f6ffc79 100644 --- a/composer.json +++ b/composer.json @@ -10,9 +10,10 @@ "require-dev": { "barryvdh/laravel-ide-helper": "^2.12", "escolalms/settings": "^0.1.91", - "nunomaduro/collision": "^5.11", - "orchestra/testbench": "^5.0|^6.0", - "phpunit/phpunit": "^9.0" + "nunomaduro/collision": ">=5.11", + "orchestra/testbench": ">=5.0", + "phpunit/phpunit": "^9.0", + "nunomaduro/larastan": "^2.0" }, "suggest": {}, "license": "MIT", diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..50a54e0 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,10 @@ +includes: + - ./vendor/nunomaduro/larastan/extension.neon + +parameters: + + paths: + - src/ + + # The level 9 is the highest level + level: 6 diff --git a/src/EscolaLmsVouchersServiceProvider.php b/src/EscolaLmsVouchersServiceProvider.php index d34de76..438f2d5 100644 --- a/src/EscolaLmsVouchersServiceProvider.php +++ b/src/EscolaLmsVouchersServiceProvider.php @@ -20,13 +20,16 @@ class EscolaLmsVouchersServiceProvider extends ServiceProvider { const CONFIG_KEY = 'escolalms_vouchers'; - public $singletons = [ + /** + * @var array + */ + public array $singletons = [ CouponServiceContract::class => CouponService::class, OrderServiceContract::class => OrderService::class, ShopServiceContract::class => ShopService::class, ]; - public function register() + public function register(): void { $this->mergeConfigFrom(__DIR__ . '/config.php', self::CONFIG_KEY); @@ -40,7 +43,7 @@ public function register() $this->app->register(AuthServiceProvider::class); } - public function boot() + public function boot(): void { $this->loadRoutesFrom(__DIR__ . '/routes.php'); $this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'coupon'); @@ -53,7 +56,7 @@ public function boot() } } - public function bootForConsole() + public function bootForConsole(): void { $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); diff --git a/src/Http/Controllers/VouchersAdminApiController.php b/src/Http/Controllers/VouchersAdminApiController.php index 0434ff3..4db86d4 100644 --- a/src/Http/Controllers/VouchersAdminApiController.php +++ b/src/Http/Controllers/VouchersAdminApiController.php @@ -28,7 +28,7 @@ public function index(ListCouponsRequest $request): JsonResponse $orderDto = OrderDto::instantiateFromRequest($request); $searchCouponsDto = $request->toDto(); $paginatedResults = $this->couponsService->searchAndPaginateCoupons($searchCouponsDto, $orderDto); - return $this->sendResponseForResource(CouponResource::collection($paginatedResults, __('Coupons search results'))); + return $this->sendResponseForResource(CouponResource::collection($paginatedResults), ('Coupons search results')); } public function create(CreateCouponRequest $request): JsonResponse diff --git a/src/Http/Controllers/VouchersApiController.php b/src/Http/Controllers/VouchersApiController.php index 5236543..3401cb8 100644 --- a/src/Http/Controllers/VouchersApiController.php +++ b/src/Http/Controllers/VouchersApiController.php @@ -3,6 +3,7 @@ namespace EscolaLms\Vouchers\Http\Controllers; use EscolaLms\Core\Http\Controllers\EscolaLmsBaseController; +use EscolaLms\Core\Models\User; use EscolaLms\Vouchers\Exceptions\CouponInactiveException; use EscolaLms\Vouchers\Exceptions\CouponNotApplicableException; use EscolaLms\Vouchers\Http\Controllers\Swagger\VouchersApiControllerSwagger; @@ -23,7 +24,9 @@ public function __construct(ShopServiceContract $shopService) public function apply(ApplyCouponRequest $request): JsonResponse { try { - $cart = $this->shopService->cartForUser($request->user()); + /** @var User $user */ + $user = $request->user(); + $cart = $this->shopService->cartForUser($user); $cartManager = $cart->cart_manager; $cartManager->setCoupon($request->getCoupon()); } catch (CouponInactiveException $ex) { @@ -36,7 +39,9 @@ public function apply(ApplyCouponRequest $request): JsonResponse public function unapply(UnapplyCouponRequest $request): JsonResponse { - $cart = $this->shopService->cartForUser($request->user()); + /** @var User $user */ + $user = $request->user(); + $cart = $this->shopService->cartForUser($user); $cartManager = $cart->cart_manager; $cartManager->removeCoupon(); return $this->sendSuccess(__("Coupon removed from cart")); diff --git a/src/Http/Requests/ApplyCouponRequest.php b/src/Http/Requests/ApplyCouponRequest.php index f93dff9..01ffea7 100644 --- a/src/Http/Requests/ApplyCouponRequest.php +++ b/src/Http/Requests/ApplyCouponRequest.php @@ -13,6 +13,9 @@ public function authorize(): bool return Gate::allows('apply', $this->getCoupon()); } + /** + * @return array + */ public function rules(): array { return [ diff --git a/src/Http/Requests/CreateCouponRequest.php b/src/Http/Requests/CreateCouponRequest.php index 8f50e7a..be4646c 100644 --- a/src/Http/Requests/CreateCouponRequest.php +++ b/src/Http/Requests/CreateCouponRequest.php @@ -19,6 +19,9 @@ public function authorize(): bool return Gate::allows('create', Coupon::class); } + /** + * @return array> + */ public function rules(): array { return [ @@ -47,7 +50,7 @@ public function rules(): array ]; } - public function withValidator(Validator $validator) + public function withValidator(Validator $validator): void { $validator->sometimes('amount', 'max:100', function ($input) { return $input->type === CouponTypeEnum::CART_PERCENT || $input->type === CouponTypeEnum::PRODUCT_PERCENT; diff --git a/src/Http/Requests/DeleteCouponRequest.php b/src/Http/Requests/DeleteCouponRequest.php index b47da73..d4528dd 100644 --- a/src/Http/Requests/DeleteCouponRequest.php +++ b/src/Http/Requests/DeleteCouponRequest.php @@ -13,6 +13,9 @@ public function authorize(): bool return Gate::allows('delete', $this->getCoupon()); } + /** + * @return array> + */ public function rules(): array { return []; diff --git a/src/Http/Requests/ListCouponsRequest.php b/src/Http/Requests/ListCouponsRequest.php index 99ed618..2dba3cb 100644 --- a/src/Http/Requests/ListCouponsRequest.php +++ b/src/Http/Requests/ListCouponsRequest.php @@ -17,6 +17,9 @@ public function authorize(): bool return Gate::allows('viewAny', Coupon::class); } + /** + * @return array> + */ public function rules(): array { return [ diff --git a/src/Http/Requests/ReadCouponRequest.php b/src/Http/Requests/ReadCouponRequest.php index 4a18567..2ffd99d 100644 --- a/src/Http/Requests/ReadCouponRequest.php +++ b/src/Http/Requests/ReadCouponRequest.php @@ -13,6 +13,9 @@ public function authorize(): bool return Gate::allows('view', $this->getCoupon()); } + /** + * @return array> + */ public function rules(): array { return []; diff --git a/src/Http/Requests/UnapplyCouponRequest.php b/src/Http/Requests/UnapplyCouponRequest.php index 7d2d747..a114369 100644 --- a/src/Http/Requests/UnapplyCouponRequest.php +++ b/src/Http/Requests/UnapplyCouponRequest.php @@ -13,6 +13,9 @@ public function authorize(): bool return Gate::allows('unapply', Coupon::class); } + /** + * @return array> + */ public function rules(): array { return []; diff --git a/src/Http/Requests/UpdateCouponRequest.php b/src/Http/Requests/UpdateCouponRequest.php index bd94920..21c2bf0 100644 --- a/src/Http/Requests/UpdateCouponRequest.php +++ b/src/Http/Requests/UpdateCouponRequest.php @@ -19,6 +19,9 @@ public function authorize(): bool return Gate::allows('update', $this->getCoupon()); } + /** + * @return array> + */ public function rules(): array { return [ @@ -47,7 +50,7 @@ public function rules(): array ]; } - public function withValidator(Validator $validator) + public function withValidator(Validator $validator): void { $validator->sometimes('amount', 'max:100', function ($input) { return $input->type === CouponTypeEnum::CART_PERCENT || $input->type === CouponTypeEnum::PRODUCT_PERCENT; diff --git a/src/Http/Resources/CartItemResource.php b/src/Http/Resources/CartItemResource.php index 3777315..4b67616 100644 --- a/src/Http/Resources/CartItemResource.php +++ b/src/Http/Resources/CartItemResource.php @@ -19,6 +19,10 @@ protected function getCartItem(): CartItem return $this->resource; } + /** + * @param $request + * @return array + */ public function toArray($request): array { return array_merge(parent::toArray($request), [ diff --git a/src/Http/Resources/CartResource.php b/src/Http/Resources/CartResource.php index d6a04c5..51a08db 100644 --- a/src/Http/Resources/CartResource.php +++ b/src/Http/Resources/CartResource.php @@ -25,6 +25,10 @@ protected function getCartItemsResourceCollection(): ResourceCollection return CartItemResource::collection($this->getCart()->items); } + /** + * @param $request + * @return array + */ public function toArray($request): array { return array_merge(parent::toArray($request), [ diff --git a/src/Http/Resources/CouponProductResource.php b/src/Http/Resources/CouponProductResource.php index eccac73..3a99e23 100644 --- a/src/Http/Resources/CouponProductResource.php +++ b/src/Http/Resources/CouponProductResource.php @@ -12,12 +12,16 @@ public function __construct(CouponProduct $couponProduct) parent::__construct($couponProduct); } - public function toArray($request) + /** + * @param $request + * @return array + */ + public function toArray($request): array { return [ - 'id' => $this->id, - 'coupon_id' => $this->coupon_id, - 'product_id' => $this->product_id, + 'id' => $this->resource->id, + 'coupon_id' => $this->resource->coupon_id, + 'product_id' => $this->resource->product_id, ]; } } diff --git a/src/Http/Resources/CouponResource.php b/src/Http/Resources/CouponResource.php index 8eed32a..cf5c65d 100644 --- a/src/Http/Resources/CouponResource.php +++ b/src/Http/Resources/CouponResource.php @@ -16,28 +16,32 @@ public function __construct(Coupon $coupon) parent::__construct($coupon); } - public function toArray($request) + /** + * @param $request + * @return array + */ + public function toArray($request): array { $couponService = app(CouponServiceContract::class); return [ - 'id' => $this->id, - 'name' => $this->name, - 'code' => $this->code, - 'type' => $this->type, - 'active' => $this->active, - 'active_from' => $this->active_from, - 'active_to' => $this->active_to, - 'limit_usage' => $this->limit_usage, - 'limit_per_user' => $this->limit_per_user, - 'min_cart_price' => $this->min_cart_price, - 'max_cart_price' => $this->max_cart_price, - 'amount' => $this->amount, - 'included_products' => ProductResource::collection($this->includedProducts), - 'excluded_products' => ProductResource::collection($this->excludedProducts), - 'users' => $this->users->map(fn (User $user) => $user->getKey())->toArray(), - 'included_categories' => CategoryResource::collection($this->includedCategories), - 'excluded_categories' => CategoryResource::collection($this->excludedCategories), - 'exclude_promotions' => $this->exclude_promotions, + 'id' => $this->resource->id, + 'name' => $this->resource->name, + 'code' => $this->resource->code, + 'type' => $this->resource->type, + 'active' => $this->resource->active, + 'active_from' => $this->resource->active_from, + 'active_to' => $this->resource->active_to, + 'limit_usage' => $this->resource->limit_usage, + 'limit_per_user' => $this->resource->limit_per_user, + 'min_cart_price' => $this->resource->min_cart_price, + 'max_cart_price' => $this->resource->max_cart_price, + 'amount' => $this->resource->amount, + 'included_products' => ProductResource::collection($this->resource->includedProducts), + 'excluded_products' => ProductResource::collection($this->resource->excludedProducts), + 'users' => $this->resource->users->map(fn (User $user) => $user->getKey())->toArray(), + 'included_categories' => CategoryResource::collection($this->resource->includedCategories), + 'excluded_categories' => CategoryResource::collection($this->resource->excludedCategories), + 'exclude_promotions' => $this->resource->exclude_promotions, 'usages' => $couponService->couponTimesUsed($this->resource), ]; } diff --git a/src/Models/Cart.php b/src/Models/Cart.php index 86087f3..2efd9f5 100644 --- a/src/Models/Cart.php +++ b/src/Models/Cart.php @@ -40,13 +40,20 @@ */ class Cart extends BaseCart { + /** @var array */ protected $guarded = ['id', 'cartManager']; + /** + * @return BelongsTo + */ public function coupon(): BelongsTo { return $this->belongsTo(Coupon::class); } + /** + * @return HasMany + */ public function items(): HasMany { return $this->hasMany(CartItem::class); diff --git a/src/Models/CartItem.php b/src/Models/CartItem.php index b934fb4..a71d034 100644 --- a/src/Models/CartItem.php +++ b/src/Models/CartItem.php @@ -47,22 +47,25 @@ */ class CartItem extends BaseCartItem { + /** + * @return BelongsTo + */ public function cart(): BelongsTo { return $this->belongsTo(Cart::class); } - public function getSubtotalAttribute() + public function getSubtotalAttribute(): float { return parent::getSubtotalAttribute() - $this->discountSubtotal; } - public function getPriceAttribute() + public function getPriceAttribute(): float|null { return parent::getPriceAttribute() - $this->discount; } - public function getBasePriceAttribute() + public function getBasePriceAttribute(): float|null { return parent::getPriceAttribute(); } diff --git a/src/Models/Category.php b/src/Models/Category.php index 732ca42..5e51547 100644 --- a/src/Models/Category.php +++ b/src/Models/Category.php @@ -47,12 +47,15 @@ class Category extends BaseCategory { use HasFactory; + /** + * @return BelongsToMany + */ public function coupons(): BelongsToMany { return $this->belongsToMany(Coupon::class, 'coupons_categories', 'category_id', 'coupon_id')->using(CouponCategory::class); } - protected static function newFactory() + protected static function newFactory(): CategoryFactory { return new CategoryFactory(); } diff --git a/src/Models/Coupon.php b/src/Models/Coupon.php index 7cd3057..6688a8d 100644 --- a/src/Models/Coupon.php +++ b/src/Models/Coupon.php @@ -2,6 +2,7 @@ namespace EscolaLms\Vouchers\Models; +use EscolaLms\Payments\Services\PaymentsService; use EscolaLms\Vouchers\Database\Factories\CouponFactory; use EscolaLms\Vouchers\Enums\CouponTypeEnum; use EscolaLms\Vouchers\Models\Product; @@ -110,7 +111,7 @@ * @OA\Items(type="boolean") * ), * ) - * + * * @property int $id * @property string|null $name * @property string $code @@ -126,6 +127,7 @@ * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property bool $exclude_promotions + * @property int $value * @property-read \Illuminate\Database\Eloquent\Collection|\EscolaLms\Vouchers\Models\Cart[] $carts * @property-read int|null $carts_count * @property-read \Illuminate\Database\Eloquent\Collection|\EscolaLms\Vouchers\Models\Category[] $categories @@ -194,57 +196,84 @@ public function getValueStringAttribute(): string } } + /** + * @return BelongsToMany + */ public function users(): BelongsToMany { return $this->belongsToMany(User::class, 'coupon_user', 'coupon_id', 'user_id')->using(CouponUser::class); } + /** + * @return BelongsToMany + */ public function products(): BelongsToMany { return $this->belongsToMany(Product::class, 'coupons_products', 'coupon_id', 'product_id')->using(CouponProduct::class); } + /** + * @return BelongsToMany + */ public function includedProducts(): BelongsToMany { return $this->products()->wherePivot('excluded', false); } + /** + * @return BelongsToMany + */ public function excludedProducts(): BelongsToMany { return $this->products()->wherePivot('excluded', true); } + /** + * @return BelongsToMany + */ public function categories(): BelongsToMany { return $this->belongsToMany(Category::class, 'coupons_categories', 'coupon_id', 'category_id')->using(CouponCategory::class); } + /** + * @return BelongsToMany + */ public function includedCategories(): BelongsToMany { return $this->categories()->wherePivot('excluded', false); } + /** + * @return BelongsToMany + */ public function excludedCategories(): BelongsToMany { return $this->categories()->wherePivot('excluded', true); } + /** + * @return HasMany + */ public function carts(): HasMany { return $this->hasMany(Cart::class); } + /** + * @return HasMany + */ public function orders(): HasMany { return $this->hasMany(Order::class); } - protected static function newFactory() + protected static function newFactory(): CouponFactory { return new CouponFactory(); } - protected static function booted() + protected static function booted(): void { parent::booted(); self::saving(function (Coupon $coupon) { diff --git a/src/Models/CouponCategory.php b/src/Models/CouponCategory.php index 4cb08b7..5029ec6 100644 --- a/src/Models/CouponCategory.php +++ b/src/Models/CouponCategory.php @@ -32,6 +32,9 @@ class CouponCategory extends Pivot { use HasTimestamps; + /** + * @var array + */ public $guarded = ['id']; protected $table = 'coupons_categories'; @@ -40,11 +43,17 @@ class CouponCategory extends Pivot 'excluded' => 'bool' ]; + /** + * @return BelongsTo + */ public function coupon(): BelongsTo { return $this->belongsTo(Coupon::class); } + /** + * @return BelongsTo + */ public function category(): BelongsTo { return $this->belongsTo(Category::class); diff --git a/src/Models/CouponProduct.php b/src/Models/CouponProduct.php index d254bff..e339a35 100644 --- a/src/Models/CouponProduct.php +++ b/src/Models/CouponProduct.php @@ -33,6 +33,9 @@ class CouponProduct extends Pivot { use HasTimestamps; + /** + * @var array + */ public $guarded = ['id']; protected $table = 'coupons_products'; @@ -41,11 +44,17 @@ class CouponProduct extends Pivot 'excluded' => 'bool' ]; + /** + * @return BelongsTo + */ public function coupon(): BelongsTo { return $this->belongsTo(Coupon::class); } + /** + * @return BelongsTo + */ public function product(): BelongsTo { return $this->belongsTo(Product::class); diff --git a/src/Models/CouponUser.php b/src/Models/CouponUser.php index 84e88c3..45c4281 100644 --- a/src/Models/CouponUser.php +++ b/src/Models/CouponUser.php @@ -27,13 +27,22 @@ */ class CouponUser extends Pivot { + /** + * @var array + */ public $guarded = ['id']; + /** + * @return BelongsTo + */ public function coupon(): BelongsTo { return $this->belongsTo(Coupon::class); } + /** + * @return BelongsTo + */ public function user(): BelongsTo { return $this->belongsTo(User::class); diff --git a/src/Models/Order.php b/src/Models/Order.php index 4158d83..03e186b 100644 --- a/src/Models/Order.php +++ b/src/Models/Order.php @@ -63,6 +63,9 @@ */ class Order extends BaseOrder { + /** + * @return BelongsTo + */ public function coupon(): BelongsTo { return $this->belongsTo(Coupon::class); diff --git a/src/Models/OrderItem.php b/src/Models/OrderItem.php index 81e578d..637ee6c 100644 --- a/src/Models/OrderItem.php +++ b/src/Models/OrderItem.php @@ -52,6 +52,9 @@ */ class OrderItem extends BaseOrderItem { + /** + * @return BelongsTo + */ public function order(): BelongsTo { return $this->belongsTo(Order::class); diff --git a/src/Models/Product.php b/src/Models/Product.php index 3c7406c..479e16c 100644 --- a/src/Models/Product.php +++ b/src/Models/Product.php @@ -70,6 +70,9 @@ */ class Product extends BaseProduct { + /** + * @return BelongsToMany + */ public function coupons(): BelongsToMany { return $this->belongsToMany(Coupon::class, 'coupons_products', 'product_id', 'coupon_id')->using(CouponProduct::class); diff --git a/src/Models/User.php b/src/Models/User.php index e11844f..4252ae1 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -80,21 +80,33 @@ */ class User extends CartUser { + /** + * @return HasMany + */ public function orders(): HasMany { return $this->hasMany(Order::class, 'user_id'); } + /** + * @return HasOne + */ public function cart(): HasOne { return $this->hasOne(Cart::class, 'user_id'); } + /** + * @return BelongsToMany + */ public function products(): BelongsToMany { return $this->belongsToMany(Product::class, 'products_users', 'user_id', 'product_id'); } + /** + * @return BelongsToMany + */ public function coupons(): BelongsToMany { return $this->belongsToMany(Coupon::class, 'coupon_user', 'user_id', 'coupon_id')->using(CouponUser::class); diff --git a/src/Policies/CouponPolicy.php b/src/Policies/CouponPolicy.php index c937d7c..b9d3521 100644 --- a/src/Policies/CouponPolicy.php +++ b/src/Policies/CouponPolicy.php @@ -19,37 +19,37 @@ public function __construct(CouponServiceContract $couponService) $this->couponService = $couponService; } - public function viewAny(User $user) + public function viewAny(User $user): bool { return $user->can(VoucherPermissionsEnum::COUPON_LIST); } - public function view(User $user, Coupon $course) + public function view(User $user, Coupon $course): bool { return $user->can(VoucherPermissionsEnum::COUPON_READ); } - public function create(User $user) + public function create(User $user): bool { return $user->can(VoucherPermissionsEnum::COUPON_CREATE); } - public function update(User $user, Coupon $coupon) + public function update(User $user, Coupon $coupon): bool { return $user->can(VoucherPermissionsEnum::COUPON_UPDATE); } - public function delete(User $user, Coupon $coupon) + public function delete(User $user, Coupon $coupon): bool { return $user->can(VoucherPermissionsEnum::COUPON_DELETE); } - public function apply(User $user, Coupon $coupon) + public function apply(User $user, Coupon $coupon): bool { return $user->can(VoucherPermissionsEnum::COUPON_USE); } - public function unapply(User $user) + public function unapply(User $user): bool { return $user->can(VoucherPermissionsEnum::COUPON_USE); } diff --git a/src/Providers/AuthServiceProvider.php b/src/Providers/AuthServiceProvider.php index 1ef8d2c..66812e3 100644 --- a/src/Providers/AuthServiceProvider.php +++ b/src/Providers/AuthServiceProvider.php @@ -12,7 +12,7 @@ class AuthServiceProvider extends ServiceProvider /** * The policy mappings for the application. * - * @var array + * @var array */ protected $policies = [ Coupon::class => CouponPolicy::class, @@ -23,7 +23,7 @@ class AuthServiceProvider extends ServiceProvider * * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); diff --git a/src/Services/CartManager.php b/src/Services/CartManager.php index 904b8d6..6a53661 100644 --- a/src/Services/CartManager.php +++ b/src/Services/CartManager.php @@ -49,6 +49,7 @@ public function discountForItem(CartItem $item): int public function removeCoupon(): self { $this->cart->refresh(); + // @phpstan-ignore-next-line $this->cart->coupon_id = null; $this->cart->save(); $this->cart->refresh(); @@ -57,6 +58,7 @@ public function removeCoupon(): self public function setCoupon(?Coupon $coupon): self { + // @phpstan-ignore-next-line if (!is_null($coupon) && !app(CouponServiceContract::class)->couponCanBeUsedOnCart($coupon, $this->cart)) { if (!app(CouponServiceContract::class)->couponIsActive($coupon)) { throw new CouponInactiveException(); @@ -65,6 +67,7 @@ public function setCoupon(?Coupon $coupon): self } $this->cart->refresh(); + // @phpstan-ignore-next-line $this->cart->coupon_id = $coupon->getKey(); $this->cart->save(); $this->cart->refresh(); diff --git a/src/Services/Contracts/CouponServiceContract.php b/src/Services/Contracts/CouponServiceContract.php index 65f1754..8fa11c0 100644 --- a/src/Services/Contracts/CouponServiceContract.php +++ b/src/Services/Contracts/CouponServiceContract.php @@ -15,11 +15,23 @@ interface CouponServiceContract { + /** + * @return LengthAwarePaginator + */ public function searchAndPaginateCoupons(CouponSearchDto $searchDto, ?OrderDto $orderDto = null): LengthAwarePaginator; public function getDiscountStrategyForCoupon(?Coupon $coupon): DiscountStrategyContract; + /** + * @param array $data + */ public function createCoupon(array $data): Coupon; + + /** + * @param Coupon $coupon + * @param array $data + * @return Coupon + */ public function updateCoupon(Coupon $coupon, array $data): Coupon; public function couponCanBeUsedOnCart(Coupon $coupon, Cart $cart): bool; @@ -27,12 +39,24 @@ public function couponIsActive(Coupon $coupon): bool; public function couponInPriceRange(Coupon $coupon, int $price): bool; public function cartContainsItemsIncludedInCoupon(Coupon $coupon, Cart $cart): bool; + + /** + * @param Coupon $coupon + * @param Cart $cart + * @return Collection + */ public function cartItemsIncludedInCoupon(Coupon $coupon, Cart $cart): Collection; public function cartItemIsIncludedInCoupon(Coupon $coupon, CartItem $item): bool; public function productIsIncludedInCoupon(Coupon $coupon, Product $product): bool; public function productCategoriesAreIncludedInCoupon(Coupon $coupon, Product $product): bool; public function cartContainsItemsNotExcludedFromCoupon(Coupon $coupon, Cart $cart): bool; + + /** + * @param Coupon $coupon + * @param Cart $cart + * @return Collection + */ public function cartItemsWithoutExcludedFromCoupon(Coupon $coupon, Cart $cart): Collection; public function cartItemIsExcludedFromCoupon(Coupon $coupon, CartItem $item): bool; public function productIsExcludedFromCoupon(Coupon $coupon, Product $product): bool; diff --git a/src/Services/CouponService.php b/src/Services/CouponService.php index c96cc6d..c67828b 100644 --- a/src/Services/CouponService.php +++ b/src/Services/CouponService.php @@ -26,6 +26,11 @@ class CouponService implements CouponServiceContract { + /** + * @param CouponSearchDto $searchDto + * @param OrderDto|null $orderDto + * @return LengthAwarePaginator + */ public function searchAndPaginateCoupons(CouponSearchDto $searchDto, ?OrderDto $orderDto = null): LengthAwarePaginator { $query = Coupon::query(); @@ -67,6 +72,10 @@ public function searchAndPaginateCoupons(CouponSearchDto $searchDto, ?OrderDto $ return $query->paginate($searchDto->getPerPage() ?? 15); } + /** + * @param array $data + * @return Coupon + */ public function createCoupon(array $data): Coupon { $coupon = new Coupon([ @@ -124,6 +133,11 @@ public function createCoupon(array $data): Coupon return $coupon->refresh(); } + /** + * @param Coupon $coupon + * @param array $data + * @return Coupon + */ public function updateCoupon(Coupon $coupon, array $data): Coupon { if (!isset($data['included_products'])) { @@ -230,9 +244,17 @@ public function cartContainsItemsIncludedInCoupon(Coupon $coupon, Cart $cart): b return ($coupon->includedProducts()->count() === 0 && $coupon->includedCategories()->count() === 0) || $this->cartItemsIncludedInCoupon($coupon, $cart)->count() > 0; } + /** + * @param Coupon $coupon + * @param Cart $cart + * @return Collection + */ public function cartItemsIncludedInCoupon(Coupon $coupon, Cart $cart): Collection { - return $cart->items->filter(fn (CartItem $item) => $this->cartItemIsIncludedInCoupon($coupon, $item)); + /** @var Collection $items */ + // @phpstan-ignore-next-line + $items = $cart->items->filter(fn (CartItem $item) => $this->cartItemIsIncludedInCoupon($coupon, $item)); + return $items; } public function cartItemIsIncludedInCoupon(Coupon $coupon, CartItem $item): bool @@ -255,9 +277,17 @@ public function cartContainsItemsNotExcludedFromCoupon(Coupon $coupon, Cart $car return ($coupon->excludedProducts()->count() === 0 && $coupon->excludedCategories()->count() === 0) || $this->cartItemsWithoutExcludedFromCoupon($coupon, $cart)->count() > 0; } + /** + * @param Coupon $coupon + * @param Cart $cart + * @return Collection + */ public function cartItemsWithoutExcludedFromCoupon(Coupon $coupon, Cart $cart): Collection { - return $cart->items->filter(fn (CartItem $item) => !$this->cartItemIsExcludedFromCoupon($coupon, $item)); + /** @var Collection $result */ + // @phpstan-ignore-next-line + $result = $cart->items->filter(fn (CartItem $item) => !$this->cartItemIsExcludedFromCoupon($coupon, $item)); + return $result; } public function cartItemIsExcludedFromCoupon(Coupon $coupon, CartItem $item): bool @@ -267,6 +297,7 @@ public function cartItemIsExcludedFromCoupon(Coupon $coupon, CartItem $item): bo public function cartFulfilPromotionConditions(Coupon $coupon, Cart $cart): bool { + // @phpstan-ignore-next-line return !$coupon->exclude_promotions || $cart->items->filter(fn (CartItem $item) => $this->productOnPromotion($coupon, $item))->count() === 0; } diff --git a/src/Services/OrderService.php b/src/Services/OrderService.php index e0c0558..d7b4f83 100644 --- a/src/Services/OrderService.php +++ b/src/Services/OrderService.php @@ -15,9 +15,12 @@ class OrderService extends BaseOrderService implements OrderServiceContract { /** @return Order */ + // @phpstan-ignore-next-line public function find($id): Model { - return Order::findOrFail($id); + /** @var Order $order */ + $order = Order::findOrFail($id); + return $order; } public function createOrderFromCart(BaseCart $cart, ?ClientDetailsDto $clientDetailsDto = null): Order @@ -28,9 +31,11 @@ public function createOrderFromCart(BaseCart $cart, ?ClientDetailsDto $clientDet public function createOrderFromCartManager(BaseCartManager $cartManager, ?ClientDetailsDto $clientDetailsDto = null): Order { if (!$cartManager instanceof CartManager) { + // @phpstan-ignore-next-line $cartManager = new CartManager($cartManager->getModel()); } $order = parent::createOrderFromCartManager($cartManager, $clientDetailsDto); + /** @var Order $order */ $order = $order instanceof Order ? $order : Order::find($order->getKey()); $order->coupon_id = optional($cartManager->getCoupon())->getKey(); $order->discount = $cartManager->additionalDiscount(); diff --git a/src/Strategies/CartFixedDiscountStrategy.php b/src/Strategies/CartFixedDiscountStrategy.php index b19b02a..d575d0b 100644 --- a/src/Strategies/CartFixedDiscountStrategy.php +++ b/src/Strategies/CartFixedDiscountStrategy.php @@ -37,7 +37,7 @@ public function calculateDiscountForItem(Cart $cart, CartItem $cartItem): int $tax = (1 + $cartItem->tax_rate / 100); $itemValue = $cartItem->basePrice * $tax; $discount = round($itemValue / $totalAmount * $maxAmount, 0); - return round($discount / $tax, 0); + return (int) round($discount / $tax, 0); } return 0; diff --git a/src/Strategies/CartPercentDiscountStrategy.php b/src/Strategies/CartPercentDiscountStrategy.php index edf35e9..f63038f 100644 --- a/src/Strategies/CartPercentDiscountStrategy.php +++ b/src/Strategies/CartPercentDiscountStrategy.php @@ -20,6 +20,7 @@ public function calculateDiscountForItem(Cart $cart, CartItem $cartItem): int if (app(CouponServiceContract::class)->cartItemIsExcludedFromCoupon($this->coupon, $cartItem)) { return 0; } - return round($this->coupon->amount * $cartItem->buyable->getBuyablePrice() / 100, 0); + // @phpstan-ignore-next-line + return (int) round($this->coupon->amount * $cartItem->buyable->getBuyablePrice() / 100, 0); } } diff --git a/src/Strategies/ProductFixedDiscountStrategy.php b/src/Strategies/ProductFixedDiscountStrategy.php index 5f8637b..34d107e 100644 --- a/src/Strategies/ProductFixedDiscountStrategy.php +++ b/src/Strategies/ProductFixedDiscountStrategy.php @@ -24,8 +24,8 @@ public function calculateDiscountForItem(Cart $cart, CartItem $cartItem): int $itemValue = $cartItem->basePrice * $tax; $discount = $itemValue < $this->coupon->amount ? $itemValue : $this->coupon->amount; if ($discount === $itemValue) { - return $cartItem->basePrice; + return (int) $cartItem->basePrice; } - return round($discount / $tax, 0); + return (int) round($discount / $tax, 0); } } diff --git a/src/Strategies/ProductPercentDiscountStrategy.php b/src/Strategies/ProductPercentDiscountStrategy.php index 2d4ffc4..7ec9d8b 100644 --- a/src/Strategies/ProductPercentDiscountStrategy.php +++ b/src/Strategies/ProductPercentDiscountStrategy.php @@ -20,6 +20,7 @@ public function calculateDiscountForItem(Cart $cart, CartItem $cartItem): int if (!app(CouponServiceContract::class)->cartItemIsIncludedInCoupon($this->coupon, $cartItem)) { return 0; } - return round($this->coupon->amount * $cartItem->buyable->getBuyablePrice() / 100, 0); + // @phpstan-ignore-next-line + return (int) round($this->coupon->amount * $cartItem->buyable->getBuyablePrice() / 100, 0); } }