first upload all files
This commit is contained in:
23
Modules/Coupon/Admin/CouponTable.php
Normal file
23
Modules/Coupon/Admin/CouponTable.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Admin;
|
||||
|
||||
use Modules\Admin\Ui\AdminTable;
|
||||
|
||||
class CouponTable extends AdminTable
|
||||
{
|
||||
/**
|
||||
* Make table response for the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function make()
|
||||
{
|
||||
return $this->newTable()
|
||||
->addColumn('discount', function ($coupon) {
|
||||
return $coupon->is_percent
|
||||
? "{$coupon->value}%"
|
||||
: $coupon->value->format();
|
||||
});
|
||||
}
|
||||
}
|
||||
66
Modules/Coupon/Admin/CouponTabs.php
Normal file
66
Modules/Coupon/Admin/CouponTabs.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Admin;
|
||||
|
||||
use Modules\Admin\Ui\Tab;
|
||||
use Modules\Admin\Ui\Tabs;
|
||||
use Modules\Coupon\Entities\Coupon;
|
||||
use Modules\Category\Entities\Category;
|
||||
|
||||
class CouponTabs extends Tabs
|
||||
{
|
||||
public function make()
|
||||
{
|
||||
$this->group('coupon_information', trans('coupon::coupons.tabs.group.coupon_information'))
|
||||
->active()
|
||||
->add($this->general())
|
||||
->add($this->usageRestrictions())
|
||||
->add($this->usageLimits());
|
||||
}
|
||||
|
||||
public function general()
|
||||
{
|
||||
return tap(new Tab('general', trans('coupon::coupons.tabs.general')), function (Tab $tab) {
|
||||
$tab->active();
|
||||
$tab->weight(5);
|
||||
|
||||
$tab->fields([
|
||||
'name',
|
||||
'code',
|
||||
'is_percent',
|
||||
'value',
|
||||
'free_shipping',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'is_active',
|
||||
]);
|
||||
|
||||
$tab->view('coupon::admin.coupons.tabs.general');
|
||||
});
|
||||
}
|
||||
|
||||
public function usageRestrictions()
|
||||
{
|
||||
return tap(new Tab('usage_restrictions', trans('coupon::coupons.tabs.usage_restrictions')), function (Tab $tab) {
|
||||
$tab->weight(10);
|
||||
$tab->fields(['minimum_spend']);
|
||||
|
||||
$coupon = Coupon::withoutGlobalScope('active')->findOrNew(request('id'));
|
||||
|
||||
$tab->view('coupon::admin.coupons.tabs.usage_restrictions', [
|
||||
'products' => $coupon->productList(),
|
||||
'excludeProducts' => $coupon->excludeProductList(),
|
||||
'categories' => Category::treeList(),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
private function usageLimits()
|
||||
{
|
||||
return tap(new Tab('usage_limits', trans('coupon::coupons.tabs.usage_limits')), function (Tab $tab) {
|
||||
$tab->weight(15);
|
||||
$tab->fields(['usage_limit_per_coupon', 'usage_limit_per_customer']);
|
||||
$tab->view('coupon::admin.coupons.tabs.usage_limits');
|
||||
});
|
||||
}
|
||||
}
|
||||
19
Modules/Coupon/Checkers/AlreadyApplied.php
Normal file
19
Modules/Coupon/Checkers/AlreadyApplied.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Cart\Facades\Cart;
|
||||
use Modules\Coupon\Exceptions\CouponAlreadyAppliedException;
|
||||
|
||||
class AlreadyApplied
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
if (Cart::couponAlreadyApplied($coupon)) {
|
||||
throw new CouponAlreadyAppliedException;
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
}
|
||||
29
Modules/Coupon/Checkers/ApplicableCategories.php
Normal file
29
Modules/Coupon/Checkers/ApplicableCategories.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Cart\Facades\Cart;
|
||||
use Modules\Coupon\Exceptions\InapplicableCouponException;
|
||||
|
||||
class ApplicableCategories
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
$coupon->load('categories');
|
||||
|
||||
if ($coupon->categories->isEmpty()) {
|
||||
return $next($coupon);
|
||||
}
|
||||
|
||||
$cartItems = Cart::items()->filter(function ($cartItem) use ($coupon) {
|
||||
return $coupon->categories->intersect($cartItem->product->categories)->isNotEmpty();
|
||||
});
|
||||
|
||||
if ($cartItems->isEmpty()) {
|
||||
throw new InapplicableCouponException;
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
}
|
||||
29
Modules/Coupon/Checkers/ApplicableProducts.php
Normal file
29
Modules/Coupon/Checkers/ApplicableProducts.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Cart\Facades\Cart;
|
||||
use Modules\Coupon\Exceptions\InapplicableCouponException;
|
||||
|
||||
class ApplicableProducts
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
$coupon->load('products');
|
||||
|
||||
if ($coupon->products->isEmpty()) {
|
||||
return $next($coupon);
|
||||
}
|
||||
|
||||
$cartItems = Cart::items()->filter(function ($cartItem) use ($coupon) {
|
||||
return $coupon->products->contains($cartItem->product);
|
||||
});
|
||||
|
||||
if ($cartItems->isEmpty()) {
|
||||
throw new InapplicableCouponException;
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
}
|
||||
18
Modules/Coupon/Checkers/CouponExists.php
Normal file
18
Modules/Coupon/Checkers/CouponExists.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Coupon\Exceptions\CouponNotExistsException;
|
||||
|
||||
class CouponExists
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
if (is_null($coupon)) {
|
||||
throw new CouponNotExistsException;
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
}
|
||||
32
Modules/Coupon/Checkers/ExcludedCategories.php
Normal file
32
Modules/Coupon/Checkers/ExcludedCategories.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Cart\Facades\Cart;
|
||||
use Modules\Coupon\Exceptions\InapplicableCouponException;
|
||||
|
||||
class ExcludedCategories
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
$coupon->load('excludeCategories');
|
||||
|
||||
if ($coupon->excludeCategories->isEmpty()) {
|
||||
return $next($coupon);
|
||||
}
|
||||
|
||||
foreach (Cart::items() as $cartItem) {
|
||||
if ($this->inExcludedCategories($coupon, $cartItem)) {
|
||||
throw new InapplicableCouponException;
|
||||
}
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
|
||||
private function inExcludedCategories($coupon, $cartItem)
|
||||
{
|
||||
return $coupon->excludeCategories->intersect($cartItem->product->categories)->isNotEmpty();
|
||||
}
|
||||
}
|
||||
32
Modules/Coupon/Checkers/ExcludedProducts.php
Normal file
32
Modules/Coupon/Checkers/ExcludedProducts.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Cart\Facades\Cart;
|
||||
use Modules\Coupon\Exceptions\InapplicableCouponException;
|
||||
|
||||
class ExcludedProducts
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
$coupon->load('excludeProducts');
|
||||
|
||||
if ($coupon->excludeProducts->isEmpty()) {
|
||||
return $next($coupon);
|
||||
}
|
||||
|
||||
foreach (Cart::items() as $cartItem) {
|
||||
if ($this->inExcludedProducts($coupon, $cartItem)) {
|
||||
throw new InapplicableCouponException;
|
||||
}
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
|
||||
private function inExcludedProducts($coupon, $cartItem)
|
||||
{
|
||||
return $coupon->excludeProducts->contains($cartItem->product);
|
||||
}
|
||||
}
|
||||
18
Modules/Coupon/Checkers/MaximumSpend.php
Normal file
18
Modules/Coupon/Checkers/MaximumSpend.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Coupon\Exceptions\MaximumSpendException;
|
||||
|
||||
class MaximumSpend
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
if ($coupon->spentMoreThanMaximumAmount()) {
|
||||
throw new MaximumSpendException($coupon->maximum_spend);
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
}
|
||||
18
Modules/Coupon/Checkers/MinimumSpend.php
Normal file
18
Modules/Coupon/Checkers/MinimumSpend.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Coupon\Exceptions\MinimumSpendException;
|
||||
|
||||
class MinimumSpend
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
if ($coupon->didNotSpendTheRequiredAmount()) {
|
||||
throw new MinimumSpendException($coupon->minimum_spend);
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
}
|
||||
18
Modules/Coupon/Checkers/UsageLimitPerCoupon.php
Normal file
18
Modules/Coupon/Checkers/UsageLimitPerCoupon.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Coupon\Exceptions\CouponUsageLimitReachedException;
|
||||
|
||||
class UsageLimitPerCoupon
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
if ($coupon->usageLimitReached()) {
|
||||
throw new CouponUsageLimitReachedException;
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
}
|
||||
18
Modules/Coupon/Checkers/UsageLimitPerCustomer.php
Normal file
18
Modules/Coupon/Checkers/UsageLimitPerCustomer.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Coupon\Exceptions\CouponUsageLimitReachedException;
|
||||
|
||||
class UsageLimitPerCustomer
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
if ($coupon->perCustomerUsageLimitReached()) {
|
||||
throw new CouponUsageLimitReachedException;
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
}
|
||||
18
Modules/Coupon/Checkers/ValidCoupon.php
Normal file
18
Modules/Coupon/Checkers/ValidCoupon.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Checkers;
|
||||
|
||||
use Closure;
|
||||
use Modules\Coupon\Exceptions\InvalidCouponException;
|
||||
|
||||
class ValidCoupon
|
||||
{
|
||||
public function handle($coupon, Closure $next)
|
||||
{
|
||||
if ($coupon->invalid()) {
|
||||
throw new InvalidCouponException;
|
||||
}
|
||||
|
||||
return $next($coupon);
|
||||
}
|
||||
}
|
||||
10
Modules/Coupon/Config/permissions.php
Normal file
10
Modules/Coupon/Config/permissions.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'admin.coupons' => [
|
||||
'index' => 'coupon::permissions.index',
|
||||
'create' => 'coupon::permissions.create',
|
||||
'edit' => 'coupon::permissions.edit',
|
||||
'destroy' => 'coupon::permissions.destroy',
|
||||
],
|
||||
];
|
||||
13
Modules/Coupon/Database/Factories/ModelFactory.php
Normal file
13
Modules/Coupon/Database/Factories/ModelFactory.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
use Modules\Coupon\Entities\Coupon;
|
||||
|
||||
$factory->define(Coupon::class, function (Faker\Generator $faker) {
|
||||
return [
|
||||
'name' => $faker->word(),
|
||||
'code' => $faker->word(),
|
||||
'value' => $faker->randomNumber(),
|
||||
'is_percent' => $faker->boolean(),
|
||||
'is_active' => $faker->boolean(),
|
||||
];
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateCouponsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('coupons', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('code')->index();
|
||||
$table->decimal('value', 18, 4)->unsigned()->nullable();
|
||||
$table->boolean('is_percent');
|
||||
$table->boolean('free_shipping');
|
||||
$table->decimal('minimum_spend', 18, 4)->unsigned()->nullable();
|
||||
$table->decimal('maximum_spend', 18, 4)->unsigned()->nullable();
|
||||
$table->integer('usage_limit_per_coupon')->unsigned()->nullable();
|
||||
$table->integer('usage_limit_per_customer')->unsigned()->nullable();
|
||||
$table->integer('used')->default(0);
|
||||
$table->boolean('is_active');
|
||||
$table->date('start_date')->nullable();
|
||||
$table->date('end_date')->nullable();
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('coupons');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateCouponTranslationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('coupon_translations', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('coupon_id')->unsigned();
|
||||
$table->string('locale');
|
||||
$table->string('name');
|
||||
|
||||
$table->unique(['coupon_id', 'locale']);
|
||||
$table->foreign('coupon_id')->references('id')->on('coupons')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('coupon_translations');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateCouponProductsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('coupon_products', function (Blueprint $table) {
|
||||
$table->integer('coupon_id')->unsigned();
|
||||
$table->integer('product_id')->unsigned();
|
||||
$table->boolean('exclude')->default(false);
|
||||
|
||||
$table->primary(['coupon_id', 'product_id']);
|
||||
$table->foreign('coupon_id')->references('id')->on('coupons')->onDelete('cascade');
|
||||
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('coupon_products');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateCouponCategoriesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('coupon_categories', function (Blueprint $table) {
|
||||
$table->integer('coupon_id')->unsigned();
|
||||
$table->integer('category_id')->unsigned();
|
||||
$table->boolean('exclude')->default(false);
|
||||
|
||||
$table->primary(['coupon_id', 'category_id', 'exclude']);
|
||||
$table->foreign('coupon_id')->references('id')->on('coupons')->onDelete('cascade');
|
||||
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('coupon_categories');
|
||||
}
|
||||
}
|
||||
19
Modules/Coupon/Database/Seeders/CouponDatabaseSeeder.php
Normal file
19
Modules/Coupon/Database/Seeders/CouponDatabaseSeeder.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Modules\Coupon\Entities\Coupon;
|
||||
|
||||
class CouponDatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
factory(Coupon::class, 10)->create();
|
||||
}
|
||||
}
|
||||
41
Modules/Coupon/Entities/Concerns/RelationList.php
Normal file
41
Modules/Coupon/Entities/Concerns/RelationList.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Entities\Concerns;
|
||||
|
||||
trait RelationList
|
||||
{
|
||||
/**
|
||||
* Get the list of the coupon applicable products.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function productList()
|
||||
{
|
||||
return $this->getItemList('products');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of the excluded products.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function excludeProductList()
|
||||
{
|
||||
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)
|
||||
{
|
||||
$items = $this->getAttribute($attribute);
|
||||
|
||||
return $items->mapWithKeys(function ($item) {
|
||||
return [$item->id => $item->name];
|
||||
})->all();
|
||||
}
|
||||
}
|
||||
41
Modules/Coupon/Entities/Concerns/SyncRelations.php
Normal file
41
Modules/Coupon/Entities/Concerns/SyncRelations.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Entities\Concerns;
|
||||
|
||||
trait SyncRelations
|
||||
{
|
||||
protected function syncProducts($products)
|
||||
{
|
||||
$this->products()->sync(
|
||||
$this->makeSyncList($products, ['exclude' => false])
|
||||
);
|
||||
}
|
||||
|
||||
protected function syncExcludeProducts($excludeProducts)
|
||||
{
|
||||
$this->excludeProducts()->sync(
|
||||
$this->makeSyncList($excludeProducts, ['exclude' => true])
|
||||
);
|
||||
}
|
||||
|
||||
protected function syncCategories($categories)
|
||||
{
|
||||
$this->categories()->sync(
|
||||
$this->makeSyncList($categories, ['exclude' => false])
|
||||
);
|
||||
}
|
||||
|
||||
protected function syncExcludeCategories($excludeCategories)
|
||||
{
|
||||
$this->excludeCategories()->sync(
|
||||
$this->makeSyncList($excludeCategories, ['exclude' => true])
|
||||
);
|
||||
}
|
||||
|
||||
private function makeSyncList($items, $pivotData)
|
||||
{
|
||||
$pivotData = array_fill(0, count($items), $pivotData);
|
||||
|
||||
return array_combine($items, $pivotData);
|
||||
}
|
||||
}
|
||||
297
Modules/Coupon/Entities/Coupon.php
Normal file
297
Modules/Coupon/Entities/Coupon.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Entities;
|
||||
|
||||
use Modules\Support\Money;
|
||||
use Modules\Cart\Facades\Cart;
|
||||
use Modules\User\Entities\User;
|
||||
use Modules\Order\Entities\Order;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Support\Eloquent\Model;
|
||||
use Modules\Coupon\Admin\CouponTable;
|
||||
use Modules\Product\Entities\Product;
|
||||
use Modules\Category\Entities\Category;
|
||||
use Modules\Support\Eloquent\Translatable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Coupon extends Model
|
||||
{
|
||||
use Translatable,
|
||||
SoftDeletes,
|
||||
Concerns\SyncRelations,
|
||||
Concerns\RelationList;
|
||||
|
||||
/**
|
||||
* The relations to eager load on every query.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $with = ['translations'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'code',
|
||||
'is_percent',
|
||||
'value',
|
||||
'free_shipping',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'is_active',
|
||||
'minimum_spend',
|
||||
'maximum_spend',
|
||||
'usage_limit_per_coupon',
|
||||
'usage_limit_per_customer',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'free_shipping' => 'boolean',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['start_date', 'end_date', 'deleted_at'];
|
||||
|
||||
/**
|
||||
* The attributes that are translatable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $translatedAttributes = ['name'];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::saved(function ($coupon) {
|
||||
$coupon->saveRelations(request()->all());
|
||||
});
|
||||
|
||||
static::addActiveGlobalScope();
|
||||
}
|
||||
|
||||
public static function findByCode($code)
|
||||
{
|
||||
return self::where(DB::raw('BINARY `code`'), $code)->first();
|
||||
}
|
||||
|
||||
public function freeShipping()
|
||||
{
|
||||
return $this->free_shipping ? $this : (object) ['free_shipping' => 0];
|
||||
}
|
||||
|
||||
public function valid()
|
||||
{
|
||||
if ($this->hasStartDate() && $this->hasEndDate()) {
|
||||
return $this->startDateIsValid() && $this->endDateIsValid();
|
||||
}
|
||||
|
||||
if ($this->hasStartDate()) {
|
||||
return $this->startDateIsValid();
|
||||
}
|
||||
|
||||
if ($this->hasEndDate()) {
|
||||
return $this->endDateIsValid();
|
||||
}
|
||||
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->used >= $this->usage_limit_per_coupon;
|
||||
}
|
||||
|
||||
public function perCustomerUsageLimitReached($customerEmail = null)
|
||||
{
|
||||
if ($this->couponHasNoUsageLimitForCustomers() ||
|
||||
$this->userIsNotLoggedInWhenAddingCouponToCart($customerEmail)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$customerEmail = $customerEmail ?: auth()->user()->email;
|
||||
|
||||
$used = $this->orders()
|
||||
->where('customer_email', $customerEmail)
|
||||
->count();
|
||||
|
||||
return $used >= $this->usage_limit_per_customer;
|
||||
}
|
||||
|
||||
private function couponHasNoUsageLimitForCustomers()
|
||||
{
|
||||
return is_null($this->usage_limit_per_customer);
|
||||
}
|
||||
|
||||
private function userIsNotLoggedInWhenAddingCouponToCart($customerEmail = null)
|
||||
{
|
||||
return is_null($customerEmail) && auth()->guest();
|
||||
}
|
||||
|
||||
public function didNotSpendTheRequiredAmount()
|
||||
{
|
||||
if (is_null($this->minimum_spend)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Cart::subTotal()->lessThan($this->minimum_spend);
|
||||
}
|
||||
|
||||
public function spentMoreThanMaximumAmount()
|
||||
{
|
||||
if (is_null($this->maximum_spend)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Cart::subTotal()->greaterThan($this->maximum_spend);
|
||||
}
|
||||
|
||||
public function products()
|
||||
{
|
||||
return $this->belongsToMany(Product::class, 'coupon_products')
|
||||
->withPivot('exclude')
|
||||
->wherePivot('exclude', false);
|
||||
}
|
||||
|
||||
public function excludeProducts()
|
||||
{
|
||||
return $this->belongsToMany(Product::class, 'coupon_products')
|
||||
->withPivot('exclude')
|
||||
->wherePivot('exclude', true);
|
||||
}
|
||||
|
||||
public function categories()
|
||||
{
|
||||
return $this->belongsToMany(Category::class, 'coupon_categories')
|
||||
->withPivot('exclude')
|
||||
->wherePivot('exclude', false);
|
||||
}
|
||||
|
||||
public function excludeCategories()
|
||||
{
|
||||
return $this->belongsToMany(Category::class, 'coupon_categories')
|
||||
->withPivot('exclude')
|
||||
->wherePivot('exclude', true);
|
||||
}
|
||||
|
||||
public function orders()
|
||||
{
|
||||
return $this->hasMany(Order::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function customers()
|
||||
{
|
||||
return $this->hasManyThrough(
|
||||
User::class,
|
||||
Order::class,
|
||||
'coupon_id',
|
||||
'id',
|
||||
'id',
|
||||
'customer_id'
|
||||
)->withTrashed();
|
||||
}
|
||||
|
||||
public function getValueAttribute($value)
|
||||
{
|
||||
if ($this->is_percent) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return Money::inDefaultCurrency($value);
|
||||
}
|
||||
|
||||
public function getMinimumSpendAttribute($minimumSpend)
|
||||
{
|
||||
if (! is_null($minimumSpend)) {
|
||||
return Money::inDefaultCurrency($minimumSpend);
|
||||
}
|
||||
}
|
||||
|
||||
public function getMaximumSpendAttribute($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
|
||||
*/
|
||||
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', []));
|
||||
}
|
||||
}
|
||||
15
Modules/Coupon/Entities/CouponTranslation.php
Normal file
15
Modules/Coupon/Entities/CouponTranslation.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Entities;
|
||||
|
||||
use Modules\Support\Eloquent\TranslationModel;
|
||||
|
||||
class CouponTranslation extends TranslationModel
|
||||
{
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['name'];
|
||||
}
|
||||
20
Modules/Coupon/Exceptions/CouponAlreadyAppliedException.php
Normal file
20
Modules/Coupon/Exceptions/CouponAlreadyAppliedException.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class CouponAlreadyAppliedException extends Exception
|
||||
{
|
||||
/**
|
||||
* Render the exception into an HTTP response.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return response()->json([
|
||||
'message' => trans('coupon::messages.already_applied'),
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
20
Modules/Coupon/Exceptions/CouponNotExistsException.php
Normal file
20
Modules/Coupon/Exceptions/CouponNotExistsException.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class CouponNotExistsException extends Exception
|
||||
{
|
||||
/**
|
||||
* Render the exception into an HTTP response.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return response()->json([
|
||||
'message' => trans('coupon::messages.not_exists'),
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class CouponUsageLimitReachedException extends Exception
|
||||
{
|
||||
/**
|
||||
* Render the exception into an HTTP response.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return response()->json([
|
||||
'message' => trans('coupon::messages.usage_limit_reached'),
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
20
Modules/Coupon/Exceptions/InapplicableCouponException.php
Normal file
20
Modules/Coupon/Exceptions/InapplicableCouponException.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InapplicableCouponException extends Exception
|
||||
{
|
||||
/**
|
||||
* Render the exception into an HTTP response.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return response()->json([
|
||||
'message' => trans('coupon::messages.inapplicable'),
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
20
Modules/Coupon/Exceptions/InvalidCouponException.php
Normal file
20
Modules/Coupon/Exceptions/InvalidCouponException.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidCouponException extends Exception
|
||||
{
|
||||
/**
|
||||
* Render the exception into an HTTP response.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return response()->json([
|
||||
'message' => trans('coupon::messages.invalid_coupon'),
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
37
Modules/Coupon/Exceptions/MaximumSpendException.php
Normal file
37
Modules/Coupon/Exceptions/MaximumSpendException.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class MaximumSpendException extends Exception
|
||||
{
|
||||
/**
|
||||
* The maximum amount that is allowed.
|
||||
*
|
||||
* @var \Modules\Support\Money
|
||||
*/
|
||||
private $money;
|
||||
|
||||
/**
|
||||
* Create a new instance of the exceptions
|
||||
*
|
||||
* @param \Modules\Support\Money $money
|
||||
*/
|
||||
public function __construct($money)
|
||||
{
|
||||
$this->money = $money;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the exception into an HTTP response.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return response()->json([
|
||||
'message' => trans('coupon::messages.maximum_spend', ['amount' => $this->money->convertToCurrentCurrency()->format()]),
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
37
Modules/Coupon/Exceptions/MinimumSpendException.php
Normal file
37
Modules/Coupon/Exceptions/MinimumSpendException.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class MinimumSpendException extends Exception
|
||||
{
|
||||
/**
|
||||
* The amount that need to spend.
|
||||
*
|
||||
* @var \Modules\Support\Money
|
||||
*/
|
||||
private $money;
|
||||
|
||||
/**
|
||||
* Create a new instance of the exceptions
|
||||
*
|
||||
* @param \Modules\Support\Money $money
|
||||
*/
|
||||
public function __construct($money)
|
||||
{
|
||||
$this->money = $money;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the exception into an HTTP response.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return response()->json([
|
||||
'message' => trans('coupon::messages.minimum_spend', ['amount' => $this->money->convertToCurrentCurrency()->format()]),
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
40
Modules/Coupon/Http/Controllers/Admin/CouponController.php
Normal file
40
Modules/Coupon/Http/Controllers/Admin/CouponController.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Http\Controllers\Admin;
|
||||
|
||||
use Modules\Coupon\Entities\Coupon;
|
||||
use Modules\Admin\Traits\HasCrudActions;
|
||||
use Modules\Coupon\Http\Requests\SaveCouponRequest;
|
||||
|
||||
class CouponController
|
||||
{
|
||||
use HasCrudActions;
|
||||
|
||||
/**
|
||||
* Model for the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Coupon::class;
|
||||
|
||||
/**
|
||||
* Label of the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label = 'coupon::coupons.coupon';
|
||||
|
||||
/**
|
||||
* View path of the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $viewPath = 'coupon::admin.coupons';
|
||||
|
||||
/**
|
||||
* Form requests for the resource.
|
||||
*
|
||||
* @var array|string
|
||||
*/
|
||||
protected $validation = SaveCouponRequest::class;
|
||||
}
|
||||
67
Modules/Coupon/Http/Controllers/CartCouponController.php
Normal file
67
Modules/Coupon/Http/Controllers/CartCouponController.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Http\Controllers;
|
||||
|
||||
use Modules\Cart\Facades\Cart;
|
||||
use Illuminate\Pipeline\Pipeline;
|
||||
use Modules\Coupon\Entities\Coupon;
|
||||
use Modules\Coupon\Checkers\ValidCoupon;
|
||||
use Modules\Coupon\Checkers\CouponExists;
|
||||
use Modules\Coupon\Checkers\MaximumSpend;
|
||||
use Modules\Coupon\Checkers\MinimumSpend;
|
||||
use Modules\Coupon\Checkers\AlreadyApplied;
|
||||
use Modules\Coupon\Checkers\ExcludedProducts;
|
||||
use Modules\Coupon\Checkers\ApplicableProducts;
|
||||
use Modules\Coupon\Checkers\ExcludedCategories;
|
||||
use Modules\Coupon\Checkers\UsageLimitPerCoupon;
|
||||
use Modules\Coupon\Checkers\ApplicableCategories;
|
||||
use Modules\Coupon\Checkers\UsageLimitPerCustomer;
|
||||
|
||||
class CartCouponController
|
||||
{
|
||||
private $checkers = [
|
||||
CouponExists::class,
|
||||
AlreadyApplied::class,
|
||||
ValidCoupon::class,
|
||||
MinimumSpend::class,
|
||||
MaximumSpend::class,
|
||||
ApplicableProducts::class,
|
||||
ExcludedProducts::class,
|
||||
ApplicableCategories::class,
|
||||
ExcludedCategories::class,
|
||||
UsageLimitPerCoupon::class,
|
||||
UsageLimitPerCustomer::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
$coupon = Coupon::findByCode(request('coupon'));
|
||||
|
||||
resolve(Pipeline::class)
|
||||
->send($coupon)
|
||||
->through($this->checkers)
|
||||
->then(function ($coupon) {
|
||||
Cart::applyCoupon($coupon);
|
||||
});
|
||||
|
||||
return Cart::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy resources by given ids.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function destroy()
|
||||
{
|
||||
Cart::removeCoupon();
|
||||
|
||||
return Cart::instance();
|
||||
}
|
||||
}
|
||||
38
Modules/Coupon/Http/Requests/SaveCouponRequest.php
Normal file
38
Modules/Coupon/Http/Requests/SaveCouponRequest.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Http\Requests;
|
||||
|
||||
use Modules\Core\Http\Requests\Request;
|
||||
|
||||
class SaveCouponRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Available attributes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $availableAttributes = 'coupon::attributes';
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'code' => 'required',
|
||||
'is_percent' => 'required|boolean',
|
||||
'value' => 'nullable|numeric|min:0|max:99999999999999',
|
||||
'free_shipping' => 'required|boolean',
|
||||
'start_date' => 'nullable|date',
|
||||
'end_date' => 'nullable|date',
|
||||
'is_active' => 'required|boolean',
|
||||
'minimum_spend' => 'nullable|numeric|min:0|max:99999999999999',
|
||||
'maximum_spend' => 'nullable|numeric|min:0|max:99999999999999',
|
||||
'usage_limit_per_coupon' => 'nullable|numeric|min:0|max:4294967295',
|
||||
'usage_limit_per_customer' => 'nullable|numeric|min:0|max:4294967295',
|
||||
];
|
||||
}
|
||||
}
|
||||
20
Modules/Coupon/Providers/CouponServiceProvider.php
Normal file
20
Modules/Coupon/Providers/CouponServiceProvider.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Providers;
|
||||
|
||||
use Modules\Coupon\Admin\CouponTabs;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Modules\Admin\Ui\Facades\TabManager;
|
||||
|
||||
class CouponServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
TabManager::register('coupons', CouponTabs::class);
|
||||
}
|
||||
}
|
||||
20
Modules/Coupon/Resources/lang/en/attributes.php
Normal file
20
Modules/Coupon/Resources/lang/en/attributes.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'name' => 'Name',
|
||||
'code' => 'Code',
|
||||
'is_percent' => 'Discount Type',
|
||||
'value' => 'Value',
|
||||
'free_shipping' => 'Free Shipping',
|
||||
'start_date' => 'Start date',
|
||||
'end_date' => 'End date',
|
||||
'is_active' => 'Status',
|
||||
'minimum_spend' => 'Minimum Spend',
|
||||
'maximum_spend' => 'Maximum Spend',
|
||||
'products' => 'Products',
|
||||
'exclude_products' => 'Exclude Products',
|
||||
'categories' => 'Categories',
|
||||
'exclude_categories' => 'Exclude Categories',
|
||||
'usage_limit_per_coupon' => 'Usage Limit Per Coupon',
|
||||
'usage_limit_per_customer' => 'Usage Limit Per Customer',
|
||||
];
|
||||
27
Modules/Coupon/Resources/lang/en/coupons.php
Normal file
27
Modules/Coupon/Resources/lang/en/coupons.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'coupon' => 'Coupon',
|
||||
'coupons' => 'Coupons',
|
||||
'table' => [
|
||||
'name' => 'Name',
|
||||
'code' => 'Code',
|
||||
'discount' => 'Discount',
|
||||
],
|
||||
'tabs' => [
|
||||
'group' => [
|
||||
'coupon_information' => 'Coupon Information',
|
||||
],
|
||||
'general' => 'General',
|
||||
'usage_restrictions' => 'Usage Restrictions',
|
||||
'usage_limits' => 'Usage Limits',
|
||||
],
|
||||
'form' => [
|
||||
'price_types' => [
|
||||
'0' => 'Fixed',
|
||||
'1' => 'Percent',
|
||||
],
|
||||
'allow_free_shipping' => 'Allow free shipping',
|
||||
'enable_the_coupon' => 'Enable the coupon',
|
||||
],
|
||||
];
|
||||
11
Modules/Coupon/Resources/lang/en/messages.php
Normal file
11
Modules/Coupon/Resources/lang/en/messages.php
Normal file
@@ -0,0 +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.',
|
||||
];
|
||||
8
Modules/Coupon/Resources/lang/en/permissions.php
Normal file
8
Modules/Coupon/Resources/lang/en/permissions.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'index' => 'Index Coupons',
|
||||
'create' => 'Create Coupons',
|
||||
'edit' => 'Edit Coupons',
|
||||
'destroy' => 'Delete Coupons',
|
||||
];
|
||||
@@ -0,0 +1,18 @@
|
||||
@extends('admin::layout')
|
||||
|
||||
@component('admin::components.page.header')
|
||||
@slot('title', trans('admin::resource.create', ['resource' => trans('coupon::coupons.coupon')]))
|
||||
|
||||
<li><a href="{{ route('admin.coupons.index') }}">{{ trans('coupon::coupons.coupons') }}</a></li>
|
||||
<li class="active">{{ trans('admin::resource.create', ['resource' => trans('coupon::coupons.coupon')]) }}</li>
|
||||
@endcomponent
|
||||
|
||||
@section('content')
|
||||
<form method="POST" action="{{ route('admin.coupons.store') }}" class="form-horizontal" id="coupon-create-form" novalidate>
|
||||
{{ csrf_field() }}
|
||||
|
||||
{!! $tabs->render(compact('coupon')) !!}
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@include('coupon::admin.coupons.partials.scripts')
|
||||
20
Modules/Coupon/Resources/views/admin/coupons/edit.blade.php
Normal file
20
Modules/Coupon/Resources/views/admin/coupons/edit.blade.php
Normal file
@@ -0,0 +1,20 @@
|
||||
@extends('admin::layout')
|
||||
|
||||
@component('admin::components.page.header')
|
||||
@slot('title', trans('admin::resource.edit', ['resource' => trans('coupon::coupons.coupon')]))
|
||||
@slot('subtitle', $coupon->name)
|
||||
|
||||
<li><a href="{{ route('admin.coupons.index') }}">{{ trans('coupon::coupons.coupons') }}</a></li>
|
||||
<li class="active">{{ trans('admin::resource.edit', ['resource' => trans('coupon::coupons.coupon')]) }}</li>
|
||||
@endcomponent
|
||||
|
||||
@section('content')
|
||||
<form method="POST" action="{{ route('admin.coupons.update', $coupon) }}" class="form-horizontal" id="coupon-edit-form" novalidate>
|
||||
{{ csrf_field() }}
|
||||
{{ method_field('put') }}
|
||||
|
||||
{!! $tabs->render(compact('coupon')) !!}
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@include('coupon::admin.coupons.partials.scripts')
|
||||
42
Modules/Coupon/Resources/views/admin/coupons/index.blade.php
Normal file
42
Modules/Coupon/Resources/views/admin/coupons/index.blade.php
Normal file
@@ -0,0 +1,42 @@
|
||||
@extends('admin::layout')
|
||||
|
||||
@component('admin::components.page.header')
|
||||
@slot('title', trans('coupon::coupons.coupons'))
|
||||
|
||||
<li class="active">{{ trans('coupon::coupons.coupons') }}</li>
|
||||
@endcomponent
|
||||
|
||||
@component('admin::components.page.index_table')
|
||||
@slot('buttons', ['create'])
|
||||
@slot('resource', 'coupons')
|
||||
@slot('name', trans('coupon::coupons.coupon'))
|
||||
|
||||
@slot('thead')
|
||||
<tr>
|
||||
@include('admin::partials.table.select_all')
|
||||
|
||||
<th data-sort>{{ trans('admin::admin.table.id') }}</th>
|
||||
<th>{{ trans('coupon::coupons.table.name') }}</th>
|
||||
<th>{{ trans('coupon::coupons.table.code') }}</th>
|
||||
<th>{{ trans('coupon::coupons.table.discount') }}</th>
|
||||
<th>{{ trans('admin::admin.table.status') }}</th>
|
||||
<th data-sort>{{ trans('admin::admin.table.created') }}</th>
|
||||
</tr>
|
||||
@endslot
|
||||
@endcomponent
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
new DataTable('#coupons-table .table', {
|
||||
columns: [
|
||||
{ data: 'checkbox', orderable: false, searchable: false, width: '3%' },
|
||||
{ data: 'id', width: '5%' },
|
||||
{ data: 'name', name: 'translations.name', orderable: false, defaultContent: '' },
|
||||
{ data: 'code' },
|
||||
{ data: 'discount', name: 'value' },
|
||||
{ data: 'status', name: 'is_active', searchable: false },
|
||||
{ data: 'created', name: 'created_at' },
|
||||
],
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,16 @@
|
||||
@include('admin::partials.selectize_remote')
|
||||
|
||||
@push('shortcuts')
|
||||
<dl class="dl-horizontal">
|
||||
<dt><code>b</code></dt>
|
||||
<dd>{{ trans('admin::admin.shortcuts.back_to_index', ['name' => trans('coupon::coupons.coupon')]) }}</dd>
|
||||
</dl>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
keypressAction([
|
||||
{ key: 'b', route: "{{ route('admin.coupons.index') }}" },
|
||||
]);
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
{{ Form::text('name', trans('coupon::attributes.name'), $errors, $coupon, ['required' => true]) }}
|
||||
{{ Form::text('code', trans('coupon::attributes.code'), $errors, $coupon, ['required' => true]) }}
|
||||
{{ 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::checkbox('is_active', trans('coupon::attributes.is_active'), trans('coupon::coupons.form.enable_the_coupon'), $errors, $coupon) }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
{{ Form::number('usage_limit_per_coupon', trans('coupon::attributes.usage_limit_per_coupon'), $errors, $coupon) }}
|
||||
{{ Form::number('usage_limit_per_customer', trans('coupon::attributes.usage_limit_per_customer'), $errors, $coupon) }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
{{ Form::number('minimum_spend', trans('coupon::attributes.minimum_spend'), $errors, $coupon, ['min' => 0]) }}
|
||||
{{ Form::number('maximum_spend', trans('coupon::attributes.maximum_spend'), $errors, $coupon, ['min' => 0]) }}
|
||||
{{ Form::select('products', trans('coupon::attributes.products'), $errors, $products, $coupon, ['class' => 'selectize prevent-creation', 'data-url' => route('admin.products.index'), 'multiple' => true]) }}
|
||||
{{ Form::select('exclude_products', trans('coupon::attributes.exclude_products'), $errors, $excludeProducts, $coupon, ['class' => 'selectize prevent-creation', 'data-url' => route('admin.products.index'), 'multiple' => true]) }}
|
||||
{{ Form::select('categories', trans('coupon::attributes.categories'), $errors, $categories, $coupon, ['class' => 'selectize prevent-creation', 'multiple' => true]) }}
|
||||
{{ Form::select('exclude_categories', trans('coupon::attributes.exclude_categories'), $errors, $categories, $coupon, ['class' => 'selectize prevent-creation', 'multiple' => true]) }}
|
||||
</div>
|
||||
</div>
|
||||
39
Modules/Coupon/Routes/admin.php
Normal file
39
Modules/Coupon/Routes/admin.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('coupons', [
|
||||
'as' => 'admin.coupons.index',
|
||||
'uses' => 'CouponController@index',
|
||||
'middleware' => 'can:admin.coupons.index',
|
||||
]);
|
||||
|
||||
Route::get('coupons/create', [
|
||||
'as' => 'admin.coupons.create',
|
||||
'uses' => 'CouponController@create',
|
||||
'middleware' => 'can:admin.coupons.create',
|
||||
]);
|
||||
|
||||
Route::post('coupons', [
|
||||
'as' => 'admin.coupons.store',
|
||||
'uses' => 'CouponController@store',
|
||||
'middleware' => 'can:admin.coupons.create',
|
||||
]);
|
||||
|
||||
Route::get('coupons/{id}/edit', [
|
||||
'as' => 'admin.coupons.edit',
|
||||
'uses' => 'CouponController@edit',
|
||||
'middleware' => 'can:admin.coupons.edit',
|
||||
]);
|
||||
|
||||
Route::put('coupons/{id}', [
|
||||
'as' => 'admin.coupons.update',
|
||||
'uses' => 'CouponController@update',
|
||||
'middleware' => 'can:admin.coupons.edit',
|
||||
]);
|
||||
|
||||
Route::delete('coupons/{ids?}', [
|
||||
'as' => 'admin.coupons.destroy',
|
||||
'uses' => 'CouponController@destroy',
|
||||
'middleware' => 'can:admin.coupons.destroy',
|
||||
]);
|
||||
6
Modules/Coupon/Routes/public.php
Normal file
6
Modules/Coupon/Routes/public.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::post('cart/coupon', 'CartCouponController@store')->name('cart.coupon.store');
|
||||
Route::delete('cart/coupon', 'CartCouponController@destroy')->name('cart.coupon.destroy');
|
||||
25
Modules/Coupon/Sidebar/SidebarExtender.php
Normal file
25
Modules/Coupon/Sidebar/SidebarExtender.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Coupon\Sidebar;
|
||||
|
||||
use Maatwebsite\Sidebar\Item;
|
||||
use Maatwebsite\Sidebar\Menu;
|
||||
use Maatwebsite\Sidebar\Group;
|
||||
use Modules\Admin\Sidebar\BaseSidebarExtender;
|
||||
|
||||
class SidebarExtender extends BaseSidebarExtender
|
||||
{
|
||||
public function extend(Menu $menu)
|
||||
{
|
||||
$menu->group(trans('admin::sidebar.content'), function (Group $group) {
|
||||
$group->item(trans('coupon::coupons.coupons'), function (Item $item) {
|
||||
$item->icon('fa fa-tags');
|
||||
$item->weight(20);
|
||||
$item->route('admin.coupons.index');
|
||||
$item->authorize(
|
||||
$this->auth->hasAccess('admin.coupons.index')
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
27
Modules/Coupon/composer.json
Normal file
27
Modules/Coupon/composer.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "fleetcart/coupon",
|
||||
"description": "The FleetCart Coupon Module..",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Envay Soft",
|
||||
"email": "envaysoft@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Coupon\\": ""
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
9
Modules/Coupon/module.json
Normal file
9
Modules/Coupon/module.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "Coupon",
|
||||
"alias": "coupon",
|
||||
"description": "Coupon management",
|
||||
"priority": 100,
|
||||
"providers": [
|
||||
"Modules\\Coupon\\Providers\\CouponServiceProvider"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user