¨4.0.1¨

This commit is contained in:
¨NW¨
2023-12-03 14:07:47 +00:00
parent c08b36d1b6
commit f35052522d
1112 changed files with 43019 additions and 24987 deletions

View File

@@ -3,13 +3,16 @@
namespace Modules\Coupon\Admin;
use Modules\Admin\Ui\AdminTable;
use Illuminate\Http\JsonResponse;
use Yajra\DataTables\Exceptions\Exception;
class CouponTable extends AdminTable
{
/**
* Make table response for the resource.
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
* @throws Exception
*/
public function make()
{

View File

@@ -18,6 +18,7 @@ class CouponTabs extends Tabs
->add($this->usageLimits());
}
public function general()
{
return tap(new Tab('general', trans('coupon::coupons.tabs.general')), function (Tab $tab) {
@@ -39,6 +40,7 @@ class CouponTabs extends Tabs
});
}
public function usageRestrictions()
{
return tap(new Tab('usage_restrictions', trans('coupon::coupons.tabs.usage_restrictions')), function (Tab $tab) {
@@ -55,6 +57,7 @@ class CouponTabs extends Tabs
});
}
private function usageLimits()
{
return tap(new Tab('usage_limits', trans('coupon::coupons.tabs.usage_limits')), function (Tab $tab) {

View File

@@ -25,6 +25,7 @@ class ExcludedCategories
return $next($coupon);
}
private function inExcludedCategories($coupon, $cartItem)
{
return $coupon->excludeCategories->intersect($cartItem->product->categories)->isNotEmpty();

View File

@@ -25,6 +25,7 @@ class ExcludedProducts
return $next($coupon);
}
private function inExcludedProducts($coupon, $cartItem)
{
return $coupon->excludeProducts->contains($cartItem->product);

View File

@@ -32,6 +32,7 @@ class CreateCouponsTable extends Migration
});
}
/**
* Reverse the migrations.
*

View File

@@ -24,6 +24,7 @@ class CreateCouponTranslationsTable extends Migration
});
}
/**
* Reverse the migrations.
*

View File

@@ -24,6 +24,7 @@ class CreateCouponProductsTable extends Migration
});
}
/**
* Reverse the migrations.
*

View File

@@ -24,6 +24,7 @@ class CreateCouponCategoriesTable extends Migration
});
}
/**
* Reverse the migrations.
*

View File

@@ -14,6 +14,7 @@ trait RelationList
return $this->getItemList('products');
}
/**
* Get the list of the excluded products.
*
@@ -24,10 +25,12 @@ trait RelationList
return $this->getItemList('excludeProducts');
}
/**
* Get the item list for the given coupon with the given attribute.
*
* @param string $attributes
*
* @return array
*/
private function getItemList($attribute)

View File

@@ -11,6 +11,7 @@ trait SyncRelations
);
}
protected function syncExcludeProducts($excludeProducts)
{
$this->excludeProducts()->sync(
@@ -18,6 +19,7 @@ trait SyncRelations
);
}
protected function syncCategories($categories)
{
$this->categories()->sync(
@@ -25,6 +27,7 @@ trait SyncRelations
);
}
protected function syncExcludeCategories($excludeCategories)
{
$this->excludeCategories()->sync(
@@ -32,6 +35,7 @@ trait SyncRelations
);
}
private function makeSyncList($items, $pivotData)
{
$pivotData = array_fill(0, count($items), $pivotData);

View File

@@ -6,6 +6,7 @@ use Modules\Support\Money;
use Modules\Cart\Facades\Cart;
use Modules\User\Entities\User;
use Modules\Order\Entities\Order;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Modules\Support\Eloquent\Model;
use Modules\Coupon\Admin\CouponTable;
@@ -72,6 +73,13 @@ class Coupon extends Model
*/
protected $translatedAttributes = ['name'];
public static function findByCode($code)
{
return self::where(DB::raw('BINARY `code`'), $code)->first();
}
/**
* Perform any actions required after the model boots.
*
@@ -86,16 +94,36 @@ class Coupon extends Model
static::addActiveGlobalScope();
}
public static function findByCode($code)
/**
* Save associated relations for the coupon.
*
* @param array $attributes
*
* @return void
*/
public function saveRelations(array $attributes)
{
return self::where(DB::raw('BINARY `code`'), $code)->first();
$this->syncProducts(array_get($attributes, 'products', []));
$this->syncExcludeProducts(array_get($attributes, 'exclude_products', []));
$this->syncCategories(array_get($attributes, 'categories', []));
$this->syncExcludeCategories(array_get($attributes, 'exclude_categories', []));
}
public function freeShipping()
{
return $this->free_shipping ? $this : (object) ['free_shipping' => 0];
return $this->free_shipping ? $this : (object)['free_shipping' => 0];
}
public function invalid()
{
return !$this->valid();
}
public function valid()
{
if ($this->hasStartDate() && $this->hasEndDate()) {
@@ -113,36 +141,13 @@ class Coupon extends Model
return true;
}
public function invalid()
{
return ! $this->valid();
}
private function hasStartDate()
{
return ! is_null($this->start_date);
}
private function hasEndDate()
{
return ! is_null($this->end_date);
}
private function startDateIsValid()
{
return today() >= $this->start_date;
}
private function endDateIsValid()
{
return today() <= $this->end_date;
}
public function usageLimitReached($customerEmail = null)
{
return $this->perCouponUsageLimitReached() || $this->perCustomerUsageLimitReached($customerEmail);
}
public function perCouponUsageLimitReached()
{
if (is_null($this->usage_limit_per_coupon)) {
@@ -152,6 +157,7 @@ class Coupon extends Model
return $this->used >= $this->usage_limit_per_coupon;
}
public function perCustomerUsageLimitReached($customerEmail = null)
{
if ($this->couponHasNoUsageLimitForCustomers() ||
@@ -169,15 +175,12 @@ class Coupon extends Model
return $used >= $this->usage_limit_per_customer;
}
private function couponHasNoUsageLimitForCustomers()
public function orders()
{
return is_null($this->usage_limit_per_customer);
return $this->hasMany(Order::class)->withTrashed();
}
private function userIsNotLoggedInWhenAddingCouponToCart($customerEmail = null)
{
return is_null($customerEmail) && auth()->guest();
}
public function didNotSpendTheRequiredAmount()
{
@@ -188,6 +191,7 @@ class Coupon extends Model
return Cart::subTotal()->lessThan($this->minimum_spend);
}
public function spentMoreThanMaximumAmount()
{
if (is_null($this->maximum_spend)) {
@@ -197,6 +201,7 @@ class Coupon extends Model
return Cart::subTotal()->greaterThan($this->maximum_spend);
}
public function products()
{
return $this->belongsToMany(Product::class, 'coupon_products')
@@ -204,6 +209,7 @@ class Coupon extends Model
->wherePivot('exclude', false);
}
public function excludeProducts()
{
return $this->belongsToMany(Product::class, 'coupon_products')
@@ -211,6 +217,7 @@ class Coupon extends Model
->wherePivot('exclude', true);
}
public function categories()
{
return $this->belongsToMany(Category::class, 'coupon_categories')
@@ -218,6 +225,7 @@ class Coupon extends Model
->wherePivot('exclude', false);
}
public function excludeCategories()
{
return $this->belongsToMany(Category::class, 'coupon_categories')
@@ -225,10 +233,6 @@ class Coupon extends Model
->wherePivot('exclude', true);
}
public function orders()
{
return $this->hasMany(Order::class)->withTrashed();
}
public function customers()
{
@@ -242,6 +246,7 @@ class Coupon extends Model
)->withTrashed();
}
public function getValueAttribute($value)
{
if ($this->is_percent) {
@@ -251,47 +256,72 @@ class Coupon extends Model
return Money::inDefaultCurrency($value);
}
public function getMinimumSpendAttribute($minimumSpend)
{
if (! is_null($minimumSpend)) {
if (!is_null($minimumSpend)) {
return Money::inDefaultCurrency($minimumSpend);
}
}
public function getMaximumSpendAttribute($maximumSpend)
{
if (! is_null($maximumSpend)) {
if (!is_null($maximumSpend)) {
return Money::inDefaultCurrency($maximumSpend);
}
}
public function getTotalAttribute($total)
{
return Money::inDefaultCurrency($total);
}
/**
* Get table data for the resource
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function table()
{
return new CouponTable($this->newQuery()->withoutGlobalScope('active'));
}
/**
* Save associated relations for the coupon.
*
* @param array $attributes
* @return void
*/
public function saveRelations(array $attributes)
{
$this->syncProducts(array_get($attributes, 'products', []));
$this->syncExcludeProducts(array_get($attributes, 'exclude_products', []));
$this->syncCategories(array_get($attributes, 'categories', []));
$this->syncExcludeCategories(array_get($attributes, 'exclude_categories', []));
private function hasStartDate()
{
return !is_null($this->start_date);
}
private function hasEndDate()
{
return !is_null($this->end_date);
}
private function startDateIsValid()
{
return today() >= $this->start_date;
}
private function endDateIsValid()
{
return today() <= $this->end_date;
}
private function couponHasNoUsageLimitForCustomers()
{
return is_null($this->usage_limit_per_customer);
}
private function userIsNotLoggedInWhenAddingCouponToCart($customerEmail = null)
{
return is_null($customerEmail) && auth()->guest();
}
}

View File

@@ -3,13 +3,14 @@
namespace Modules\Coupon\Exceptions;
use Exception;
use Illuminate\Http\Response;
class CouponAlreadyAppliedException extends Exception
{
/**
* Render the exception into an HTTP response.
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function render()
{

View File

@@ -3,13 +3,14 @@
namespace Modules\Coupon\Exceptions;
use Exception;
use Illuminate\Http\Response;
class CouponNotExistsException extends Exception
{
/**
* Render the exception into an HTTP response.
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function render()
{

View File

@@ -3,15 +3,16 @@
namespace Modules\Coupon\Exceptions;
use Exception;
use Illuminate\Http\JsonResponse;
class CouponUsageLimitReachedException extends Exception
{
/**
* Render the exception into an HTTP response.
*
* @return \Illuminate\Http\Response
* @return JsonResponse
*/
public function render()
public function render(): JsonResponse
{
return response()->json([
'message' => trans('coupon::messages.usage_limit_reached'),

View File

@@ -3,13 +3,14 @@
namespace Modules\Coupon\Exceptions;
use Exception;
use Illuminate\Http\Response;
class InapplicableCouponException extends Exception
{
/**
* Render the exception into an HTTP response.
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function render()
{

View File

@@ -3,13 +3,14 @@
namespace Modules\Coupon\Exceptions;
use Exception;
use Illuminate\Http\Response;
class InvalidCouponException extends Exception
{
/**
* Render the exception into an HTTP response.
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function render()
{

View File

@@ -3,30 +3,34 @@
namespace Modules\Coupon\Exceptions;
use Exception;
use Modules\Support\Money;
use Illuminate\Http\Response;
class MaximumSpendException extends Exception
{
/**
* The maximum amount that is allowed.
*
* @var \Modules\Support\Money
* @var Money
*/
private $money;
/**
* Create a new instance of the exceptions
*
* @param \Modules\Support\Money $money
* @param Money $money
*/
public function __construct($money)
{
$this->money = $money;
}
/**
* Render the exception into an HTTP response.
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function render()
{

View File

@@ -3,30 +3,34 @@
namespace Modules\Coupon\Exceptions;
use Exception;
use Modules\Support\Money;
use Illuminate\Http\Response;
class MinimumSpendException extends Exception
{
/**
* The amount that need to spend.
*
* @var \Modules\Support\Money
* @var Money
*/
private $money;
/**
* Create a new instance of the exceptions
*
* @param \Modules\Support\Money $money
* @param Money $money
*/
public function __construct($money)
{
$this->money = $money;
}
/**
* Render the exception into an HTTP response.
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function render()
{

View File

@@ -2,6 +2,7 @@
namespace Modules\Coupon\Http\Controllers;
use Illuminate\Http\Request;
use Modules\Cart\Facades\Cart;
use Illuminate\Pipeline\Pipeline;
use Modules\Coupon\Entities\Coupon;
@@ -19,7 +20,7 @@ use Modules\Coupon\Checkers\UsageLimitPerCustomer;
class CartCouponController
{
private $checkers = [
private array $checkers = [
CouponExists::class,
AlreadyApplied::class,
ValidCoupon::class,
@@ -33,32 +34,40 @@ class CartCouponController
UsageLimitPerCustomer::class,
];
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @param Request $request
*
* @return false|string
*/
public function store()
{
$cartWithCoupon = null;
$coupon = Coupon::findByCode(request('coupon'));
resolve(Pipeline::class)
->send($coupon)
->through($this->checkers)
->then(function ($coupon) {
->then(function ($coupon) use (&$cartWithCoupon) {
Cart::applyCoupon($coupon);
$cartWithCoupon = json_encode(Cart::instance());
Cart::removeCoupon();
});
return Cart::instance();
return $cartWithCoupon;
}
/**
* Destroy resources by given ids.
*
* @return void
* @return \Modules\Cart\Cart
*/
public function destroy()
public function destroy(): \Modules\Cart\Cart
{
Cart::removeCoupon();

View File

@@ -13,6 +13,7 @@ class SaveCouponRequest extends Request
*/
protected $availableAttributes = 'coupon::attributes';
/**
* Get the validation rules that apply to the request.
*

View File

@@ -1,11 +1,11 @@
<?php
return [
'not_exists' => 'The coupon does not exist.',
'already_applied' => 'The coupon has been already applied.',
'invalid_coupon' => 'The coupon is not valid.',
'minimum_spend' => 'You need to spend at least :amount to apply this coupon.',
'maximum_spend' => 'You need to spend less than :amount to apply this coupon.',
'inapplicable' => 'This coupon is not applicable to your cart.',
'usage_limit_reached' => 'The coupon usage limit has been reached.',
'not_exists' => 'The coupon does not exist',
'already_applied' => 'The coupon has been already applied',
'invalid_coupon' => 'The coupon is not valid',
'minimum_spend' => 'You need to spend at least :amount to apply this coupon',
'maximum_spend' => 'You need to spend less than :amount to apply this coupon',
'inapplicable' => 'This coupon is not applicable to your cart',
'usage_limit_reached' => 'The coupon usage limit has been reached',
];

View File

@@ -26,7 +26,7 @@
@endcomponent
@push('scripts')
<script>
<script type="module">
new DataTable('#coupons-table .table', {
columns: [
{ data: 'checkbox', orderable: false, searchable: false, width: '3%' },

View File

@@ -8,7 +8,7 @@
@endpush
@push('scripts')
<script>
<script type="module">
keypressAction([
{ key: 'b', route: "{{ route('admin.coupons.index') }}" },
]);

View File

@@ -5,8 +5,8 @@
{{ Form::select('is_percent', trans('coupon::attributes.is_percent'), $errors, trans('coupon::coupons.form.price_types'), $coupon) }}
{{ Form::number('value', trans('coupon::attributes.value'), $errors, $coupon, ['min' => 0]) }}
{{ Form::checkbox('free_shipping', trans('coupon::attributes.free_shipping'), trans('coupon::coupons.form.allow_free_shipping'), $errors, $coupon->freeShipping()) }}
{{ Form::text('start_date', trans('coupon::attributes.start_date'), $errors, $coupon, ['class' => 'datetime-picker', 'data-default-date' => $coupon->start_date]) }}
{{ Form::text('end_date', trans('coupon::attributes.end_date'), $errors, $coupon, ['class' => 'datetime-picker', 'data-default-date' => $coupon->end_date]) }}
{{ Form::text('start_date', trans('coupon::attributes.start_date'), $errors, $coupon, ['class' => 'datetime-picker', 'data-default-date' => $coupon->start_date, 'data-time' => true]) }}
{{ Form::text('end_date', trans('coupon::attributes.end_date'), $errors, $coupon, ['class' => 'datetime-picker', 'data-default-date' => $coupon->end_date, 'data-time' => true]) }}
{{ Form::checkbox('is_active', trans('coupon::attributes.is_active'), trans('coupon::coupons.form.enable_the_coupon'), $errors, $coupon) }}
</div>
</div>

View File

@@ -37,3 +37,9 @@ Route::delete('coupons/{ids?}', [
'uses' => 'CouponController@destroy',
'middleware' => 'can:admin.coupons.destroy',
]);
Route::get('coupons/index/table', [
'as' => 'admin.coupons.table',
'uses' => 'CouponController@table',
'middleware' => 'can:admin.coupons.index',
]);