merge bagisto/master; resolved conflicts; added unit tests for database logic

This commit is contained in:
MonaHartdegen 2019-12-13 13:43:22 +01:00
commit f90bb1d7fd
102 changed files with 1451 additions and 781 deletions

View File

@ -13,6 +13,7 @@ DB_PORT=3306
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
DB_PREFIX=
BROADCAST_DRIVER=log
CACHE_DRIVER=file

45
.env.testing Normal file
View File

@ -0,0 +1,45 @@
APP_NAME=Bagisto
APP_ENV=local
APP_VERSION=0.1.8
APP_KEY=base64:NFtGjjFAqET6RlX3PVC/gFpzHb4jK1OxDc3cuU5Asz4=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=bagisto_test
DB_USERNAME=root
DB_PASSWORD=root
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
SESSION_LIFETIME=20
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
SHOP_MAIL_FROM=
ADMIN_MAIL_TO=
fixer_api_key=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

View File

@ -1,5 +1,5 @@
---
name: "🧐 Support Question"
name: " Support Question"
about: 'This repository is only for reporting bugs or problems. If you need help, see:
https://github.com/bagisto/bagisto#documentation'
---
@ -8,4 +8,4 @@ This repository is only for reporting bugs or issues. If you need support, pleas
1. Create support ticket on https://bagisto.uvdesk.com
Thanks!
Thanks!

4
.gitignore vendored
View File

@ -5,7 +5,6 @@
/public/js
/public/vendor
/public/themes
/storage/*.key
/vendor
/.idea
/.vscode
@ -20,3 +19,6 @@ composer.lock
yarn.lock
package-lock.json
yarn.lock
.php_cs.cache
storage/
storage/*.key

11
codeception.yml Normal file
View File

@ -0,0 +1,11 @@
paths:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
actor_suffix: Tester
extensions:
enabled:
- Codeception\Extension\RunFailed

View File

@ -2,9 +2,10 @@
"name": "bagisto/bagisto",
"description": "Bagisto Laravel ECommerce",
"keywords": [
"framework",
"laravel"
"framework",
"laravel"
],
"license": "MIT",
"type": "project",
"require": {
@ -17,7 +18,6 @@
"ext-pdo_mysql": "*",
"ext-tokenizer": "*",
"astrotomic/laravel-translatable": "^11.0.0",
"alkhachatryan/laravel-web-console": "1.5.1",
"barryvdh/laravel-dompdf": "0.8.3",
"doctrine/dbal": "2.9.2",
"fideloper/proxy": "^4.0",
@ -34,94 +34,111 @@
"prettus/l5-repository": "2.6.32",
"tymon/jwt-auth": "1.0.0-rc.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.1",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
"laravel/dusk": "^4.0",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0",
"phpunit/phpunit": "^7.0"
"codeception/codeception": "3.1.*",
"barryvdh/laravel-debugbar": "^3.1",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
"laravel/dusk": "^4.0",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0",
"phpunit/phpunit": "^7.0"
},
"replace": {
"bagisto/laravel-user": "v0.1.0",
"bagisto/laravel-admin": "v0.1.0",
"bagisto/laravel-ui": "v0.1.0",
"bagisto/laravel-core": "v0.1.0",
"bagisto/laravel-attribute": "v0.1.0",
"bagisto/laravel-checkout": "v0.1.0",
"bagisto/laravel-customer": "v0.1.0",
"bagisto/laravel-inventory": "v0.1.0",
"bagisto/laravel-category": "v0.1.0",
"bagisto/laravel-product": "v0.1.0",
"bagisto/laravel-shop": "v0.1.0",
"bagisto/laravel-theme": "v0.1.0",
"bagisto/laravel-shipping": "v0.1.0",
"bagisto/laravel-payment": "v0.1.0",
"bagisto/laravel-sales": "v0.1.0",
"bagisto/laravel-tax": "v0.1.0",
"bagisto/laravel-api": "v0.1.0",
"bagisto/laravel-paypal": "v0.1.0",
"bagisto/laravel-discount": "v0.1.0"
"bagisto/laravel-user": "v0.1.0",
"bagisto/laravel-admin": "v0.1.0",
"bagisto/laravel-ui": "v0.1.0",
"bagisto/laravel-core": "v0.1.0",
"bagisto/laravel-attribute": "v0.1.0",
"bagisto/laravel-checkout": "v0.1.0",
"bagisto/laravel-customer": "v0.1.0",
"bagisto/laravel-inventory": "v0.1.0",
"bagisto/laravel-category": "v0.1.0",
"bagisto/laravel-product": "v0.1.0",
"bagisto/laravel-shop": "v0.1.0",
"bagisto/laravel-theme": "v0.1.0",
"bagisto/laravel-shipping": "v0.1.0",
"bagisto/laravel-payment": "v0.1.0",
"bagisto/laravel-sales": "v0.1.0",
"bagisto/laravel-tax": "v0.1.0",
"bagisto/laravel-api": "v0.1.0",
"bagisto/laravel-paypal": "v0.1.0",
"bagisto/laravel-discount": "v0.1.0"
},
"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/",
"Webkul\\User\\": "packages/Webkul/User/src",
"Webkul\\Admin\\": "packages/Webkul/Admin/src",
"Webkul\\Ui\\": "packages/Webkul/Ui/src",
"Webkul\\Category\\": "packages/Webkul/Category/src",
"Webkul\\Checkout\\": "packages/Webkul/Checkout/src",
"Webkul\\Attribute\\": "packages/Webkul/Attribute/src",
"Webkul\\Shop\\": "packages/Webkul/Shop/src",
"Webkul\\Core\\": "packages/Webkul/Core/src",
"Webkul\\Customer\\": "packages/Webkul/Customer/src",
"Webkul\\Inventory\\": "packages/Webkul/Inventory/src",
"Webkul\\Product\\": "packages/Webkul/Product/src",
"Webkul\\Theme\\": "packages/Webkul/Theme/src",
"Webkul\\Shipping\\": "packages/Webkul/Shipping/src",
"Webkul\\Payment\\": "packages/Webkul/Payment/src",
"Webkul\\Paypal\\": "packages/Webkul/Paypal/src",
"Webkul\\Sales\\": "packages/Webkul/Sales/src",
"Webkul\\Tax\\": "packages/Webkul/Tax/src",
"Webkul\\API\\": "packages/Webkul/API",
"Webkul\\Discount\\": "packages/Webkul/Discount/src",
"Webkul\\CMS\\": "packages/Webkul/CMS/src"
}
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/",
"Webkul\\User\\": "packages/Webkul/User/src",
"Webkul\\Admin\\": "packages/Webkul/Admin/src",
"Webkul\\Ui\\": "packages/Webkul/Ui/src",
"Webkul\\Category\\": "packages/Webkul/Category/src",
"Webkul\\Checkout\\": "packages/Webkul/Checkout/src",
"Webkul\\Attribute\\": "packages/Webkul/Attribute/src",
"Webkul\\Shop\\": "packages/Webkul/Shop/src",
"Webkul\\Core\\": "packages/Webkul/Core/src",
"Webkul\\Customer\\": "packages/Webkul/Customer/src",
"Webkul\\Inventory\\": "packages/Webkul/Inventory/src",
"Webkul\\Product\\": "packages/Webkul/Product/src",
"Webkul\\Theme\\": "packages/Webkul/Theme/src",
"Webkul\\Shipping\\": "packages/Webkul/Shipping/src",
"Webkul\\Payment\\": "packages/Webkul/Payment/src",
"Webkul\\Paypal\\": "packages/Webkul/Paypal/src",
"Webkul\\Sales\\": "packages/Webkul/Sales/src",
"Webkul\\Tax\\": "packages/Webkul/Tax/src",
"Webkul\\API\\": "packages/Webkul/API",
"Webkul\\Discount\\": "packages/Webkul/Discount/src",
"Webkul\\CMS\\": "packages/Webkul/CMS/src"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
"psr-4": {
"Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar",
"laravel/dusk"
]
}
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar",
"laravel/dusk"
]
}
},
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover"
]
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate",
"Webkul\\Core\\Events\\ComposerEvents::postCreateProject"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover"
],
"test": [
"set -e",
"@php artisan migrate:fresh --env=testing",
"vendor/bin/codecept run unit",
"vendor/bin/codecept run functional"
]
},
"config": {
"preferred-install": "stable",
"sort-packages": true,
"optimize-autoloader": true
"preferred-install": "stable",
"sort-packages": true,
"optimize-autoloader": true
},
"minimum-stability": "dev"
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@ -36,7 +36,7 @@ return [
'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'prefix' => env('DB_PREFIX'),
],
'mysql' => [
@ -49,7 +49,7 @@ return [
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix' => env('DB_PREFIX'),
'strict' => false,
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
],
@ -62,7 +62,7 @@ return [
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix' => env('DB_PREFIX'),
'schema' => 'public',
'sslmode' => 'prefer',
],

View File

@ -1,51 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| User accesses
|--------------------------------------------------------------------------
|
| This file contains the connection settings with your custom user.
|
|
| !!!!! ATTENTION !!!!!
|
| These user credentials ARE NOT your server user credentials.
| You can type here everything you want.
| This method of custom login is a small addition in the protection.
| Anyway you can disable it.
|
|
|
| The preferred type of editing the accesses is to edit your .env file
| Kindly add the following lines in your .env file
| CONSOLE_USER_NAME={name}
| CONSOLE_USER_PASSWORD={password}
|
*/
// Disable login (don't ask for credentials, be careful)
'no_login' => false,
// Single-user credentials (REQUIRED)
'user' => [
'name' => env('CONSOLE_USER_NAME', 'root'),
'password' => env('CONSOLE_USER_PASSWORD', 'root'),
],
// Multi-user credentials (OPTIONAL)
// Example: 'user' => 'password', 'user1' => 'password1'
'accounts' => [
// 'user' => 'password',
],
// Hash incoming password
// By default it's sha256
'password_hash_algorithm' => '',
// Home directory (multi-user mode supported)
// Example: 'home_dir' => '/tmp';
// 'home_dir' => array('user1' => '/home/user1', 'user2' => '/home/user2');
'home_dir' => '',
];

View File

@ -3,6 +3,7 @@
namespace Webkul\API\Http\Resources\Catalog;
use Illuminate\Http\Resources\Json\JsonResource;
use Webkul\Product\Helpers\ProductType;
class Product extends JsonResource
{
@ -44,7 +45,7 @@ class Product extends JsonResource
'base_image' => $this->productImageHelper->getProductBaseImage($product),
'variants' => Self::collection($this->variants),
'in_stock' => $product->haveSufficientQuantity(1),
$this->mergeWhen($product->type == 'configurable', [
$this->mergeWhen($product->getTypeInstance()->isComposite(), [
'super_attributes' => Attribute::collection($product->super_attributes),
]),
'special_price' => $this->when(

View File

@ -28,19 +28,19 @@ class AddressDataGrid extends DataGrid
* @var object
*/
protected $customer;
/**
* Create a new controller instance.
*
* @param Webkul\Customer\Repositories\CustomerRepository $customer
* @return void
*/
public function __construct(
public function __construct(
Customer $customer
)
{
parent::__construct();
$this->customer = $customer;
}
@ -52,7 +52,7 @@ class AddressDataGrid extends DataGrid
$queryBuilder = DB::table('customer_addresses as ca')
->leftJoin('countries', 'ca.country', '=', 'countries.code')
->leftJoin('customers as c', 'ca.customer_id', '=', 'c.id')
->addSelect('ca.id as address_id', 'ca.address1', 'ca.country', DB::raw('countries.name as country_name'), 'ca.state', 'ca.city', 'ca.postcode', 'ca.phone', 'ca.default_address')
->addSelect('ca.id as address_id', 'ca.address1', 'ca.country', DB::raw(''.DB::getTablePrefix().'countries.name as country_name'), 'ca.state', 'ca.city', 'ca.postcode', 'ca.phone', 'ca.default_address')
->where('c.id', $customer->id);
$queryBuilder = $queryBuilder->leftJoin('country_states', function($qb) {
@ -62,13 +62,13 @@ class AddressDataGrid extends DataGrid
$queryBuilder
->groupBy('ca.id')
->addSelect(DB::raw('country_states.default_name as state_name'));
->addSelect(DB::raw(''.DB::getTablePrefix().'country_states.default_name as state_name'));
$this->addFilter('address_id', 'ca.id');
$this->addFilter('address1', 'ca.address1');
$this->addFilter('city', 'ca.city');
$this->addFilter('state_name', DB::raw('country_states.default_name'));
$this->addFilter('country_name', DB::raw('countries.name'));
$this->addFilter('state_name', DB::raw(''.DB::getTablePrefix().'country_states.default_name'));
$this->addFilter('country_name', DB::raw(''.DB::getTablePrefix().'countries.name'));
$this->addFilter('postcode', 'ca.postcode');
$this->addFilter('default_address', 'ca.default_address');

View File

@ -21,7 +21,7 @@ class CategoryDataGrid extends DataGrid
{
$queryBuilder = DB::table('categories as cat')
->select('cat.id as category_id', 'ct.name', 'cat.position', 'cat.status', 'ct.locale',
DB::raw('COUNT(DISTINCT pc.product_id) as count'))
DB::raw('COUNT(DISTINCT '.DB::getTablePrefix().'pc.product_id) as count'))
->leftJoin('category_translations as ct', function($leftJoin) {
$leftJoin->on('cat.id', '=', 'ct.category_id')
->where('ct.locale', app()->getLocale());

View File

@ -25,10 +25,10 @@ class CustomerDataGrid extends DataGrid
$queryBuilder = DB::table('customers')
->leftJoin('customer_groups', 'customers.customer_group_id', '=', 'customer_groups.id')
->addSelect('customers.id as customer_id', 'customers.email', 'customer_groups.name', 'customers.phone', 'customers.gender', 'status')
->addSelect(DB::raw('CONCAT(customers.first_name, " ", customers.last_name) as full_name'));
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'customers.first_name, " ", '.DB::getTablePrefix().'customers.last_name) as full_name'));
$this->addFilter('customer_id', 'customers.id');
$this->addFilter('full_name', DB::raw('CONCAT(customers.first_name, " ", customers.last_name)'));
$this->addFilter('full_name', DB::raw('CONCAT('.DB::getTablePrefix().'customers.first_name, " ", '.DB::getTablePrefix().'customers.last_name)'));
$this->addFilter('phone', 'customers.phone');
$this->addFilter('gender', 'customers.gender');

View File

@ -29,11 +29,11 @@ class OrderDataGrid extends DataGrid
->where('order_address_billing.address_type', 'billing');
})
->addSelect('orders.id','orders.increment_id', 'orders.base_sub_total', 'orders.base_grand_total', 'orders.created_at', 'channel_name', 'status')
->addSelect(DB::raw('CONCAT(order_address_billing.first_name, " ", order_address_billing.last_name) as billed_to'))
->addSelect(DB::raw('CONCAT(order_address_shipping.first_name, " ", order_address_shipping.last_name) as shipped_to'));
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name) as billed_to'))
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name) as shipped_to'));
$this->addFilter('billed_to', DB::raw('CONCAT(order_address_billing.first_name, " ", order_address_billing.last_name)'));
$this->addFilter('shipped_to', DB::raw('CONCAT(order_address_shipping.first_name, " ", order_address_shipping.last_name)'));
$this->addFilter('billed_to', DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name)'));
$this->addFilter('shipped_to', DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name)'));
$this->addFilter('increment_id', 'orders.increment_id');
$this->addFilter('created_at', 'orders.created_at');

View File

@ -26,9 +26,9 @@ class OrderRefundDataGrid extends DataGrid
$leftJoin->on('order_address_billing.order_id', '=', 'orders.id')
->where('order_address_billing.address_type', 'billing');
})
->addSelect(DB::raw('CONCAT(order_address_billing.first_name, " ", order_address_billing.last_name) as billed_to'));
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name) as billed_to'));
$this->addFilter('billed_to', DB::raw('CONCAT(order_address_billing.first_name, " ", order_address_billing.last_name)'));
$this->addFilter('billed_to', DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name)'));
$this->addFilter('id', 'refunds.id');
$this->addFilter('increment_id', 'orders.increment_id');
$this->addFilter('state', 'refunds.state');

View File

@ -27,7 +27,7 @@ class OrderShipmentsDataGrid extends DataGrid
->leftJoin('orders as ors', 'shipments.order_id', '=', 'ors.id')
->leftJoin('inventory_sources as is', 'shipments.inventory_source_id', '=', 'is.id')
->select('shipments.id as shipment_id', 'ors.increment_id as shipment_order_id', 'shipments.total_qty as shipment_total_qty', 'is.name as inventory_source_name', 'ors.created_at as order_date', 'shipments.created_at as shipment_created_at')
->addSelect(DB::raw('CONCAT(order_address_shipping.first_name, " ", order_address_shipping.last_name) as shipped_to'));
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name) as shipped_to'));
$this->addFilter('shipment_id', 'shipments.id');
$this->addFilter('shipment_order_id', 'ors.increment_id');
@ -35,7 +35,7 @@ class OrderShipmentsDataGrid extends DataGrid
$this->addFilter('inventory_source_name', 'is.name');
$this->addFilter('order_date', 'ors.created_at');
$this->addFilter('shipment_created_at', 'shipments.created_at');
$this->addFilter('shipped_to', DB::raw('CONCAT(order_address_shipping.first_name, " ", order_address_shipping.last_name)'));
$this->addFilter('shipped_to', DB::raw(''.DB::getTablePrefix().'CONCAT(order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name)'));
$this->setQueryBuilder($queryBuilder);
}

View File

@ -25,7 +25,7 @@ class ProductDataGrid extends DataGrid
->leftJoin('products', 'product_flat.product_id', '=', 'products.id')
->leftJoin('attribute_families', 'products.attribute_family_id', '=', 'attribute_families.id')
->leftJoin('product_inventories', 'product_flat.product_id', '=', 'product_inventories.product_id')
->select('product_flat.product_id as product_id', 'product_flat.sku as product_sku', 'product_flat.name as product_name', 'products.type as product_type', 'product_flat.status', 'product_flat.price', 'attribute_families.name as attribute_family', DB::raw('SUM(product_inventories.qty) as quantity'))
->select('product_flat.product_id as product_id', 'product_flat.sku as product_sku', 'product_flat.name as product_name', 'products.type as product_type', 'product_flat.status', 'product_flat.price', 'attribute_families.name as attribute_family', DB::raw('SUM('.DB::getTablePrefix().'product_inventories.qty) as quantity'))
->where('channel', core()->getCurrentChannelCode())
->where('locale', app()->getLocale())
->groupBy('product_flat.product_id');
@ -152,7 +152,7 @@ class ProductDataGrid extends DataGrid
public function prepareMassActions() {
$this->addMassAction([
'type' => 'delete',
'label' => 'Delete',
'label' => 'Delete',
'action' => route('admin.catalog.products.massdelete'),
'method' => 'DELETE'
]);

View File

@ -120,6 +120,10 @@ class ConfigurationController extends Controller
{
Event::fire('core.configuration.save.before');
$this->validate(request(), [
'general.design.admin_logo.logo_image' => 'required|mimes:jpeg,bmp,png,jpg'
]);
$this->coreConfigRepository->create(request()->all());
Event::fire('core.configuration.save.after');

View File

@ -186,7 +186,7 @@ class DashboardController extends Controller
->where('order_items.created_at', '>=', $this->startDate)
->where('order_items.created_at', '<=', $this->endDate)
->addSelect(DB::raw('SUM(qty_invoiced - qty_refunded) as total_qty_invoiced'))
->addSelect(DB::raw('COUNT(products.id) as total_products'))
->addSelect(DB::raw('COUNT('.DB::getTablePrefix().'products.id) as total_products'))
->addSelect('order_items.id', 'categories.id as category_id', 'category_translations.name')
->groupBy('categories.id')
->havingRaw('SUM(qty_invoiced - qty_refunded) > 0')

View File

@ -1,24 +0,0 @@
<?php
namespace Webkul\Admin\Http\Controllers\Development;
use Alkhachatryan\LaravelWebConsole\LaravelWebConsole;
use Webkul\Admin\Http\Controllers\Controller;
/**
* WebConsole controller
*
* @author Alexey Khachatryan <info@khachatryan.org>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class WebConsoleController extends Controller
{
/**
* Show the Web Console.
*
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
public function index(){
return LaravelWebConsole::show();
}
}

View File

@ -765,10 +765,7 @@ Route::group(['middleware' => ['web']], function () {
Route::prefix('development')->group(function () {
Route::get('/', 'Webkul\Admin\Http\Controllers\Development\DashboardController@index')
->name('admin.development.index');
Route::get('webconsole', 'Webkul\Admin\Http\Controllers\Development\WebConsoleController@index')
->name('admin.development.webconsole');
});
});
});
});
});

View File

@ -729,7 +729,6 @@ return [
],
'development' => [
'title' => 'تطوير',
'webconsole' => 'وحدة تحكم الويب',
]
],

View File

@ -838,7 +838,6 @@ return [
],
'development' => [
'title' => 'Development',
'webconsole' => 'Web Console',
]
],

View File

@ -764,7 +764,6 @@ return [
],
'development' => [
'title' => 'توسعه',
'webconsole' => 'کنسول وب',
]
],

View File

@ -755,7 +755,6 @@ return [
],
'development' => [
'title' => 'Desenvolvimento',
'webconsole' => 'Console da Web',
]
],
'customers' => [

View File

@ -64,7 +64,7 @@
</select>
@if ($familyId)
<input type="hidden" name="type" value="configurable"/>
<input type="hidden" name="type" value="{{ app('request')->input('type') }}"/>
@endif
<span class="control-error" v-if="errors.has('type')">@{{ errors.first('type') }}</span>
</div>

View File

@ -48,6 +48,8 @@
<div class="page-content">
<tabs>
{!! view_render_event('sales.order.tabs.before', ['order' => $order]) !!}
<tab name="{{ __('admin::app.sales.orders.info') }}" :selected="true">
<div class="sale-container">
@ -506,8 +508,10 @@
</div>
</tab>
{!! view_render_event('sales.order.tabs.after', ['order' => $order]) !!}
</tabs>
</div>
</div>
@stop
@stop

View File

@ -241,14 +241,14 @@
<tbody>
@foreach ($order->items as $item)
<tr>
<td>{{ $item->type == 'configurable' ? $item->child->sku : $item->sku }}</td>
<td>{{ Webkul\Product\Helpers\ProductType::hasVariants($item->type) ? $item->child->sku : $item->sku }}</td>
<td>
{{ $item->name }}
@if (isset($item->additional['attributes']))
<div class="item-options">
@foreach ($item->additional['attributes'] as $attribute)
<b>{{ $attribute['attribute_name'] }} : </b>{{ $attribute['option_label'] }}</br>
@endforeach
@ -417,7 +417,7 @@
if (! response.data) {
window.flashMessages = [{
'type': 'alert-error',
'message': "{{ __('admin::app.sales.refunds.invalid-qty') }}"
'message': "{{ __('admin::app.sales.refunds.invalid-qty') }}"
}];
this_this.$root.addFlashMessages()

View File

@ -13,16 +13,7 @@
</div>
<div class="page-content">
<a href="{{ route('admin.development.webconsole') }}" target="_blank">
<div class="rad-info-box">
<p>
<span class="heading">{{ __('admin::app.settings.development.webconsole') }}</span>
</p>
<p>
<i class="icon cms-icon"></i>
</p>
</div>
</a>
</div>
</div>
@stop

View File

@ -40,7 +40,9 @@ use Webkul\Core\Repositories\LocaleRepository as Locale;
/**
* Pass the class instance through admin middleware
*/
$this->middleware('auth:admin');
// $this->middleware('auth:admin');
$this->middleware('admin');
/**
* Channel repository instance

View File

@ -0,0 +1,19 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Category\Models\Category;
$factory->define(Category::class, function (Faker $faker, array $attributes) {
return [
'status' => 1,
'position' => $faker->randomDigit,
'parent_id' => 1,
];
});
$factory->state(Category::class, 'inactive', [
'status' => 0,
]);

View File

@ -2,6 +2,7 @@
namespace Webkul\Category\Providers;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
use Webkul\Category\Models\CategoryProxy;
use Webkul\Category\Observers\CategoryObserver;
@ -18,6 +19,8 @@ class CategoryServiceProvider extends ServiceProvider
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
CategoryProxy::observe(CategoryObserver::class);
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
}
/**
@ -29,4 +32,15 @@ class CategoryServiceProvider extends ServiceProvider
{
}
/**
* Register factories.
*
* @param string $path
* @return void
*/
protected function registerEloquentFactoriesFrom($path): void
{
$this->app->make(EloquentFactory::class)->load($path);
}
}

View File

@ -211,7 +211,13 @@ class Core
if ($locale)
return $locale;
return $locale = $this->localeRepository->findOneByField('code', app()->getLocale());
$locale = $this->localeRepository->findOneByField('code', app()->getLocale());
if(!$locale) {
$locale = $this->localeRepository->findOneByField('code', config('app.fallback_locale'));
}
return $locale;
}
/**

View File

@ -0,0 +1,19 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Core\Models\Locale;
$factory->define(Locale::class, function (Faker $faker, array $attributes) {
return [
'code' => $faker->languageCode,
'name' => $faker->country,
'direction' => 'ltr',
];
});
$factory->state(Category::class, 'rtl', [
'direction' => 'rtl',
]);

View File

@ -0,0 +1,15 @@
<?php
namespace Webkul\Core\Events;
use Symfony\Component\Console\Output\ConsoleOutput;
class ComposerEvents
{
public static function postCreateProject()
{
$output = new ConsoleOutput();
$output->writeln(file_get_contents(__DIR__ . '/../Templates/on-boarding.php'));
}
}

View File

@ -2,6 +2,7 @@
namespace Webkul\Core\Providers;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\AliasLoader;
@ -36,6 +37,8 @@ class CoreServiceProvider extends ServiceProvider
]);
SliderProxy::observe(SliderObserver::class);
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
}
/**
@ -61,4 +64,15 @@ class CoreServiceProvider extends ServiceProvider
return app()->make(Core::class);
});
}
/**
* Register factories.
*
* @param string $path
* @return void
*/
protected function registerEloquentFactoriesFrom($path): void
{
$this->app->make(EloquentFactory::class)->load($path);
}
}

View File

@ -0,0 +1,14 @@
____ _ _
| __ ) __ _ __ _(_)___| |_ ___
| _ \ / _` |/ _` | / __| __/ _ \
| |_) | (_| | (_| | \__ \ || (_) |
|____/ \__,_|\__, |_|___/\__\___/
|___/
</>
Welcome to the <info>Bagisto</info> project! Bagisto Community is an <comment>open-source e-commerce ecosystem </comment>
which is built on top of Laravel and Vue.js.
Made with 💖 by the Bagisto Team. Happy helping :)

View File

@ -59,15 +59,12 @@ class WishlistController extends Controller
/**
* Displays the listing resources if the customer having items in wishlist.
*
*
* @return \Illuminate\View\View
*/
public function index()
{
$wishlistItems = $this->wishlistRepository->findWhere([
'channel_id' => core()->getCurrentChannel()->id,
'customer_id' => auth()->guard('customer')->user()->id]
);
$wishlistItems = $this->wishlistRepository->getCustomerWhishlist();
return view($this->_config['view'])->with('items', $wishlistItems);
}

View File

@ -62,8 +62,7 @@ class WishlistRepository extends Repository
return $this->model->find($id)->item_wishlist;
}
/**
/**
* get customer wishlist Items.
*
* @return mixed

View File

@ -6,6 +6,7 @@ use Webkul\Discount\Repositories\CatalogRuleRepository as CatalogRule;
use Webkul\Discount\Repositories\CatalogRuleProductsRepository as CatalogRuleProducts;
use Webkul\Discount\Repositories\CatalogRuleProductsPriceRepository as CatalogRuleProductsPrice;
use Webkul\Discount\Helpers\Catalog\ConvertXToProductId as ConvertX;
use Webkul\Product\Helpers\ProductType;
use Webkul\Product\Repositories\ProductRepository as Product;
use Webkul\Discount\Helpers\Catalog\Sale;
@ -451,7 +452,7 @@ class Apply extends Sale
foreach ($productIDs as $productID) {
$product = $products->find($productID);
if ($product->type == 'configurable') {
if (ProductType::hasVariants($product->type)) {
$variants = $product->variants;
foreach($variants as $variant) {

View File

@ -6,6 +6,7 @@ use Webkul\Discount\Repositories\CatalogRuleRepository as CatalogRule;
use Webkul\Attribute\Repositories\AttributeRepository as Attribute;
use Webkul\Attribute\Repositories\AttributeOptionRepository as AttributeOption;
use Webkul\Category\Repositories\CategoryRepository as Category;
use Webkul\Product\Helpers\ProductType;
use Webkul\Product\Repositories\ProductRepository as Product;
use Webkul\Product\Models\ProductAttributeValue as ProductAttributeValue;
@ -110,7 +111,7 @@ class ConvertXToProductId
$products = collect();
foreach ($attributeOptions as $attributeOption) {
if (isset($attributeOption->type) && $attributeOption->name != null && $attributeOption->condition != null && $attributeOption->value != [] && $attributeOption->type != null) {
if (isset($attributeOption->type) && $attributeOption->attribute != null && $attributeOption->condition != null && $attributeOption->value != [] && $attributeOption->type != null) {
$selectedOptions = $attributeOption->value;
if ($attributeOption->type == 'select' || $attributeOption->type == 'multiselect') {
@ -154,17 +155,17 @@ class ConvertXToProductId
if ($testCondition == '{}') {
$foundProducts = $this->product->findWhere([
['sku', 'like', '%' . $testValue . '%'],
['type', '!=', 'configurable']
])->flatten()->all();
['type', 'NOT IN', ProductType::getAllTypesHavingVariants()]
])->flatten()->all();
} else if ($testCondition == '!{}') {
$foundProducts = $this->product->findWhere([
['sku', 'not like', '%' . $testValue . '%'],
['type', '!=', 'configurable']
['type', 'NOT IN', ProductType::getAllTypesHavingVariants()]
])->flatten()->all();
} else if ($testCondition == '=') {
$foundProducts = $this->product->findWhere([
['sku', '=', $testValue],
['type', '!=', 'configurable']
['type', 'NOT IN', ProductType::getAllTypesHavingVariants()]
])->flatten()->all();
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Webkul\Product\Helpers;
use Webkul\Product\Type\AbstractType;
class ProductType extends AbstractProduct
{
/**
* Checks if a ProductType may have variants
*
* @param string $typeKey as defined in config('product_types)
*
* @return bool whether ProductType is able to have variants
*/
public static function hasVariants(string $typeKey): bool
{
/** @var AbstractType $type */
$type = app(config('product_types.' . $typeKey . '.class'));
return $type->hasVariants();
}
/**
* Get all ProductTypes that are allowed to have variants
*
* @return array of product_types->keys
*/
public static function getAllTypesHavingVariants(): array
{
foreach (config('product_types') as $type) {
if (self::hasVariants($type['key'])) {
array_push($havingVariants, $type['key']);
}
}
return $havingVariants;
}
}

View File

@ -4,6 +4,7 @@ namespace Webkul\Product\Http\Controllers;
use Illuminate\Support\Facades\Event;
use Webkul\Product\Http\Requests\ProductForm;
use Webkul\Product\Helpers\ProductType;
use Webkul\Category\Repositories\CategoryRepository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductDownloadableLinkRepository;
@ -140,13 +141,13 @@ class ProductController extends Controller
public function store()
{
if (! request()->get('family')
&& request()->input('type') == 'configurable'
&& ProductType::hasVariants(request()->input('type'))
&& request()->input('sku') != '') {
return redirect(url()->current() . '?type=' . request()->input('type') . '&family=' . request()->input('attribute_family_id') . '&sku=' . request()->input('sku'));
}
if (request()->input('type') == 'configurable'
if (ProductType::hasVariants(request()->input('type'))
&& (! request()->has('super_attributes')
|| ! count(request()->get('super_attributes')))) {

View File

@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Attribute\Repositories\AttributeOptionRepository;
use Webkul\Product\Helpers\ProductType;
use Webkul\Product\Repositories\ProductFlatRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
use Webkul\Product\Models\ProductAttributeValue;
@ -148,7 +149,7 @@ class ProductFlat
{
$this->createFlat($product);
if ($product->type == 'configurable') {
if (ProductType::hasVariants($product->type)) {
foreach ($product->variants()->get() as $variant) {
$this->createFlat($variant, $product);
}

View File

@ -3,6 +3,7 @@
namespace Webkul\Product\Type;
use Illuminate\Support\Facades\Storage;
use phpDocumentor\Reflection\Types\Boolean;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
@ -57,7 +58,7 @@ abstract class AbstractType
/**
* Product Image helper instance
*
*
* @var ProductImage
*/
protected $productImageHelper;
@ -104,6 +105,13 @@ abstract class AbstractType
*/
protected $canBeMovedFromWishlistToCart = true;
/**
* Has child products aka variants
*
* @var boolean
*/
protected $hasVariants = false;
/**
* Create a new product type instance.
*
@ -159,17 +167,21 @@ abstract class AbstractType
$product->update($data);
foreach ($product->attribute_family->custom_attributes as $attribute) {
if ($attribute->type == 'boolean')
if ($attribute->type == 'boolean') {
$data[$attribute->code] = isset($data[$attribute->code]) && $data[$attribute->code] ? 1 : 0;
}
if (! isset($data[$attribute->code]))
if (! isset($data[$attribute->code])) {
continue;
}
if ($attribute->type == 'date' && $data[$attribute->code] == '')
if ($attribute->type == 'date' && $data[$attribute->code] == '') {
$data[$attribute->code] = null;
}
if ($attribute->type == 'multiselect' || $attribute->type == 'checkbox')
if ($attribute->type == 'multiselect' || $attribute->type == 'checkbox') {
$data[$attribute->code] = implode(",", $data[$attribute->code]);
}
if ($attribute->type == 'image' || $attribute->type == 'file') {
$data[$attribute->code] = gettype($data[$attribute->code]) == 'object'
@ -198,16 +210,18 @@ abstract class AbstractType
], $attributeValue->id
);
if ($attribute->type == 'image' || $attribute->type == 'file')
if ($attribute->type == 'image' || $attribute->type == 'file') {
Storage::delete($attributeValue->text_value);
}
}
}
$route = request()->route() ? request()->route()->getName() : "";
$route = request()->route() ? request()->route()->getName() : "";
if ($route != 'admin.catalog.products.massupdate') {
if (isset($data['categories']))
if (isset($data['categories'])) {
$product->categories()->sync($data['categories']);
}
$product->up_sells()->sync($data['up_sell'] ?? []);
@ -243,9 +257,10 @@ abstract class AbstractType
*/
public function isSaleable()
{
if (! $this->product->status)
if (! $this->product->status) {
return false;
}
return true;
}
@ -260,7 +275,7 @@ abstract class AbstractType
}
/**
* Return true if this product can have inventory
* Return true if this product can be composite
*
* @return boolean
*/
@ -269,6 +284,16 @@ abstract class AbstractType
return $this->isComposite;
}
/**
* Return true if this product can have variants
*
* @return bool
*/
public function hasVariants(): bool
{
return $this->hasVariants;
}
/**
* @param integer $qty
* @return bool
@ -277,7 +302,7 @@ abstract class AbstractType
{
return $this->haveSufficientQuantity;
}
/**
* Return true if this product can have inventory
*
@ -310,8 +335,9 @@ abstract class AbstractType
->pluck('id');
foreach ($this->product->inventories as $inventory) {
if (is_numeric($index = $channelInventorySourceIds->search($inventory->inventory_source_id)))
if (is_numeric($index = $channelInventorySourceIds->search($inventory->inventory_source_id))) {
$total += $inventory->qty;
}
}
$orderedInventory = $this->product->ordered_inventories()
@ -344,11 +370,13 @@ abstract class AbstractType
*/
public function getEditableAttributes($group = null, $skipSuperAttribute = true)
{
if ($skipSuperAttribute)
if ($skipSuperAttribute) {
$this->skipAttributes = array_merge($this->product->super_attributes->pluck('code')->toArray(), $this->skipAttributes);
}
if (! $group)
if (! $group) {
return $this->product->attribute_family->custom_attributes()->whereNotIn('attributes.code', $this->skipAttributes)->get();
}
return $group->custom_attributes()->whereNotIn('code', $this->skipAttributes)->get();
}
@ -380,8 +408,9 @@ abstract class AbstractType
*/
public function getMinimalPrice()
{
if ($this->haveSpecialPrice())
if ($this->haveSpecialPrice()) {
return $this->product->special_price;
}
return $this->product->price;
}
@ -421,11 +450,13 @@ abstract class AbstractType
*/
public function haveSpecialPrice()
{
if (is_null($this->product->special_price) || ! (float) $this->product->special_price)
if (is_null($this->product->special_price) || ! (float) $this->product->special_price) {
return false;
}
if (core()->isChannelDateInInterval($this->product->special_price_from, $this->product->special_price_to))
if (core()->isChannelDateInInterval($this->product->special_price_from, $this->product->special_price_to)) {
return true;
}
return false;
}
@ -479,8 +510,9 @@ abstract class AbstractType
$data = $this->getQtyRequest($data);
if (! $this->haveSufficientQuantity($data['quantity']))
if (! $this->haveSufficientQuantity($data['quantity'])) {
return trans('shop::app.checkout.cart.quantity.inventory_warning');
}
$price = $this->getFinalPrice();
@ -514,12 +546,13 @@ abstract class AbstractType
*/
public function getQtyRequest($data)
{
if ($item = Cart::getItemByProduct(['additional' => $data]))
if ($item = Cart::getItemByProduct(['additional' => $data])) {
$data['quantity'] += $item->quantity;
}
return $data;
}
/**
*
* @param array $options1
@ -546,7 +579,7 @@ abstract class AbstractType
return true;
}
/**
* Returns additional information for items
*
@ -590,8 +623,9 @@ abstract class AbstractType
{
$price = $item->product->getTypeInstance()->getFinalPrice();
if ($price == $item->base_price)
if ($price == $item->base_price) {
return;
}
$item->base_price = $price;
$item->price = core()->convertPrice($price);

View File

@ -22,7 +22,7 @@ class Configurable extends AbstractType
/**
* These blade files will be included in product edit page
*
*
* @var array
*/
protected $additionalViews = [
@ -47,6 +47,13 @@ class Configurable extends AbstractType
*/
protected $showQuantityBox = true;
/**
* Has child products aka variants
*
* @var boolean
*/
protected $hasVariants = true;
/**
* @param array $data
* @return Product
@ -136,9 +143,15 @@ class Configurable extends AbstractType
];
}
$typeOfVariants = 'simple';
$productInstance = app(config('product_types.' . $product->type . '.class'));
if (isset($productInstance->variantsType) && ! in_array($productInstance->variantsType , ['bundle', 'configurable', 'grouped'])) {
$typeOfVariants = $productInstance->variantsType;
}
$variant = $this->productRepository->getModel()->create([
'parent_id' => $product->id,
'type' => 'simple',
'type' => $typeOfVariants,
'attribute_family_id' => $product->attribute_family_id,
'sku' => $data['sku'],
]);
@ -416,7 +429,7 @@ class Configurable extends AbstractType
return $products;
}
/**
*
* @param array $options1
@ -451,7 +464,7 @@ class Configurable extends AbstractType
];
}
return $data;
return $data;
}
/**

View File

@ -3,6 +3,7 @@
namespace Webkul\Sales\Repositories;
use Illuminate\Container\Container as App;
use Illuminate\Support\Facades\Event;
use Webkul\Core\Eloquent\Repository;
use Webkul\Sales\Contracts\OrderItem;
@ -51,7 +52,7 @@ class OrderItemRepository extends Repository
$totalInvoiced = $baseTotalInvoiced = 0;
$taxInvoiced = $baseTaxInvoiced = 0;
$totalRefunded = $baseTotalRefunded = 0;
$taxRefunded = $baseTaxRefunded = 0;

View File

@ -3,15 +3,15 @@
namespace Webkul\Sales\Repositories;
use Illuminate\Container\Container as App;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Webkul\Core\Eloquent\Repository;
use Webkul\Sales\Contracts\Order;
use Webkul\Sales\Repositories\OrderItemRepository;
use Webkul\Core\Models\CoreConfig;
use Webkul\Sales\Contracts\Order;
use Webkul\Sales\Models\Order as OrderModel;
/**
* Order Reposotory
* Order Repository
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
@ -35,16 +35,16 @@ class OrderRepository extends Repository
/**
* Create a new repository instance.
*
* @param Webkul\Sales\Repositories\OrderItemRepository $orderItemRepository
* @param Webkul\Sales\Repositories\DownloadableLinkPurchasedRepository $downloadableLinkPurchasedRepository
* @param Webkul\Sales\Repositories\OrderItemRepository $orderItemRepository
* @param Webkul\Sales\Repositories\DownloadableLinkPurchasedRepository $downloadableLinkPurchasedRepository
*
* @return void
*/
public function __construct(
OrderItemRepository $orderItemRepository,
DownloadableLinkPurchasedRepository $downloadableLinkPurchasedRepository,
App $app
)
{
) {
$this->orderItemRepository = $orderItemRepository;
$this->downloadableLinkPurchasedRepository = $downloadableLinkPurchasedRepository;
@ -58,13 +58,14 @@ class OrderRepository extends Repository
* @return Mixed
*/
function model()
public function model()
{
return Order::class;
}
/**
* @param array $data
*
* @return mixed
*/
public function create(array $data)
@ -95,12 +96,15 @@ class OrderRepository extends Repository
$order->payment()->create($data['payment']);
if (isset($data['shipping_address']))
if (isset($data['shipping_address'])) {
$order->addresses()->create($data['shipping_address']);
}
$order->addresses()->create($data['billing_address']);
foreach ($data['items'] as $item) {
Event::fire('checkout.order.orderitem.save.before', $data);
$orderItem = $this->orderItemRepository->create(array_merge($item, ['order_id' => $order->id]));
if (isset($item['children']) && $item['children']) {
@ -112,6 +116,8 @@ class OrderRepository extends Repository
$this->orderItemRepository->manageInventory($orderItem);
$this->downloadableLinkPurchasedRepository->saveLinks($orderItem, 'available');
Event::fire('checkout.order.orderitem.save.after', $data);
}
Event::fire('checkout.order.save.after', $order);
@ -128,20 +134,23 @@ class OrderRepository extends Repository
/**
* @param int $orderId
*
* @return mixed
*/
public function cancel($orderId)
{
$order = $this->findOrFail($orderId);
if (! $order->canCancel())
if (! $order->canCancel()) {
return false;
}
Event::fire('sales.order.cancel.before', $order);
foreach ($order->items as $item) {
if (! $item->qty_to_cancel)
if (! $item->qty_to_cancel) {
continue;
}
$orderItems = [];
@ -152,11 +161,12 @@ class OrderRepository extends Repository
} else {
$orderItems[] = $item;
}
foreach ($orderItems as $orderItem) {
if ($orderItem->product)
if ($orderItem->product) {
$this->orderItemRepository->returnQtyToProductInventory($orderItem);
}
if ($orderItem->qty_ordered) {
$orderItem->qty_canceled += $orderItem->qty_to_cancel;
$orderItem->save();
@ -188,19 +198,21 @@ class OrderRepository extends Repository
{
$config = new CoreConfig();
$invoiceNumberPrefix = $config->where('code','=',"sales.orderSettings.order_number.order_number_prefix")->first()
? $config->where('code','=',"sales.orderSettings.order_number.order_number_prefix")->first()->value : false;
$invoiceNumberLength = $config->where('code','=',"sales.orderSettings.order_number.order_number_length")->first()
? $config->where('code','=',"sales.orderSettings.order_number.order_number_length")->first()->value : false;
$invoiceNumberSuffix = $config->where('code','=',"sales.orderSettings.order_number.order_number_suffix")->first()
? $config->where('code','=',"sales.orderSettings.order_number.order_number_suffix")->first()->value: false;
foreach ([
'Prefix' => 'prefix',
'Length' => 'length',
'Suffix' => 'suffix',
] as $varSuffix => $confKey) {
$var = "invoiceNumber{$varSuffix}";
$$var = $config
->where('code', '=', "sales.orderSettings.order_number.order_number_{$confKey}")
->first() ?: false;
}
$lastOrder = $this->model->orderBy('id', 'desc')->limit(1)->first();
$lastId = $lastOrder ? $lastOrder->id : 0;
if ($invoiceNumberLength && ( $invoiceNumberPrefix || $invoiceNumberSuffix) ) {
if ($invoiceNumberLength && ($invoiceNumberPrefix || $invoiceNumberSuffix)) {
$invoiceNumber = $invoiceNumberPrefix . sprintf("%0{$invoiceNumberLength}d", 0) . ($lastId + 1) . $invoiceNumberSuffix;
} else {
$invoiceNumber = $lastId + 1;
@ -211,13 +223,14 @@ class OrderRepository extends Repository
/**
* @param mixed $order
*
* @return void
*/
public function isInCompletedState($order)
{
$totalQtyOrdered = $totalQtyInvoiced = $totalQtyShipped = $totalQtyRefunded = $totalQtyCanceled = 0;
foreach ($order->items()->get() as $item) {
foreach ($order->items()->get() as $item) {
$totalQtyOrdered += $item->qty_ordered;
$totalQtyInvoiced += $item->qty_invoiced;
@ -233,14 +246,16 @@ class OrderRepository extends Repository
if ($totalQtyOrdered != ($totalQtyRefunded + $totalQtyCanceled)
&& $totalQtyOrdered == $totalQtyInvoiced + $totalQtyCanceled
&& $totalQtyOrdered == $totalQtyShipped + $totalQtyRefunded + $totalQtyCanceled)
&& $totalQtyOrdered == $totalQtyShipped + $totalQtyRefunded + $totalQtyCanceled) {
return true;
}
return false;
}
/**
* @param mixed $order
*
* @return void
*/
public function isInCanceledState($order)
@ -257,13 +272,14 @@ class OrderRepository extends Repository
/**
* @param mixed $order
*
* @return void
*/
public function isInClosedState($order)
{
$totalQtyOrdered = $totalQtyRefunded = $totalQtyCanceled = 0;
foreach ($order->items()->get() as $item) {
foreach ($order->items()->get() as $item) {
$totalQtyOrdered += $item->qty_ordered;
$totalQtyRefunded += $item->qty_refunded;
$totalQtyCanceled += $item->qty_canceled;
@ -274,19 +290,22 @@ class OrderRepository extends Repository
/**
* @param mixed $order
*
* @return void
*/
public function updateOrderStatus($order)
{
$status = 'processing';
if ($this->isInCompletedState($order))
if ($this->isInCompletedState($order)) {
$status = 'completed';
}
if ($this->isInCanceledState($order))
if ($this->isInCanceledState($order)) {
$status = 'canceled';
else if ($this->isInClosedState($order))
} elseif ($this->isInClosedState($order)) {
$status = 'closed';
}
$order->status = $status;
$order->save();
@ -294,6 +313,7 @@ class OrderRepository extends Repository
/**
* @param mixed $order
*
* @return mixed
*/
public function collectTotals($order)
@ -352,4 +372,4 @@ class OrderRepository extends Repository
return $order;
}
}
}

View File

@ -0,0 +1,38 @@
<?php
use Webkul\Sales\Models\Order;
use Webkul\Sales\Repositories\OrderRepository;
class OrderRepositoryCest
{
private $repository;
public function _before()
{
// @see https://stackoverflow.com/a/8457580
$reflection = new ReflectionClass(OrderRepository::class);
$property = $reflection->getProperty('model');
$property->setAccessible(true);
$this->repository = $reflection->newInstanceWithoutConstructor();
$property->setValue($this->repository, new Order());
}
public function testGenerateIncrementIdOnEmptyDatabase(UnitTester $I)
{
$result = $this->repository->generateIncrementId();
$I->assertEquals(1, $result);
}
public function testGenerateIncrementIdOnFilledDatabase(UnitTester $I)
{
$order = new Order(['id' => rand(666, 1337)]);
$order->save();
$result = $this->repository->generateIncrementId();
$I->assertEquals($order->id + 1, $result);
}
}

View File

@ -22,7 +22,7 @@ class DownloadableProductDataGrid extends DataGrid
$queryBuilder = DB::table('downloadable_link_purchased')
->leftJoin('orders', 'downloadable_link_purchased.order_id', '=', 'orders.id')
->addSelect('downloadable_link_purchased.*', 'orders.increment_id')
->addSelect(DB::raw('(downloadable_link_purchased.download_bought - downloadable_link_purchased.download_used) as remaining_downloads'))
->addSelect(DB::raw('('.DB::getTablePrefix().'downloadable_link_purchased.download_bought - '.DB::getTablePrefix().'downloadable_link_purchased.download_used) as remaining_downloads'))
->where('downloadable_link_purchased.customer_id', auth()->guard('customer')->user()->id);
$this->addFilter('status', 'downloadable_link_purchased.status');
@ -97,7 +97,7 @@ class DownloadableProductDataGrid extends DataGrid
'wrapper' => function ($value) {
if (! $value->download_bought)
return trans('shop::app.customer.account.downloadable_products.unlimited');
return $value->remaining_downloads;
},
]);

View File

@ -60,7 +60,7 @@ class OrderController extends Controller
/**
* Display a listing of the resource.
*
* @return \Illuminate\View\View
* @return \Illuminate\View\View
*/
public function index()
{
@ -71,7 +71,7 @@ class OrderController extends Controller
* Show the view for the specified resource.
*
* @param int $id
* @return \Illuminate\View\View
* @return \Illuminate\View\View
*/
public function view($id)
{

View File

@ -296,7 +296,7 @@
else
paymentHtml = Vue.compile(response.data.html)
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] + 1;
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] - 1;
this_this.current_step = this_this.step_numbers[response.data.jump_to_section];
shippingMethods = response.data.shippingMethods;
@ -320,7 +320,7 @@
this_this.disable_button = false;
paymentHtml = Vue.compile(response.data.html)
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] + 1;
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] - 1;
this_this.current_step = this_this.step_numbers[response.data.jump_to_section];
paymentMethods = response.data.paymentMethods;
@ -344,7 +344,7 @@
this_this.disable_button = false;
reviewHtml = Vue.compile(response.data.html)
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] + 1;
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] - 1;
this_this.current_step = this_this.step_numbers[response.data.jump_to_section];
this_this.getOrderSummary();

View File

@ -19,12 +19,10 @@
<span class="checkmark"></span>
{{-- <label class="radio-view" for="{{ $payment['method'] }}"></label> --}}
<span class="payment-method method-label">
<b>{{ $payment['method_title'] }}</b>
</span>
</label>
<span class="payment-method method-label">
<b>{{ $payment['method_title'] }}</b>
</span>
</div>
<div class="line-two mt-5">

View File

@ -19,7 +19,7 @@
@if (count($reviews) > 1)
<div class="account-action">
<a href="{{ route('customer.review.deleteall') }}">{{ __('shop::app.wishlist.deleteall') }}</a>
<a href="{{ route('customer.review.deleteall') }}">{{ __('shop::app.customer.account.wishlist.deleteall') }}</a>
</div>
@endif

View File

@ -22,7 +22,7 @@
<a href="{{ route('customer.wishlist.removeall') }}">{{ __('shop::app.customer.account.wishlist.deleteall') }}</a>
</div>
@endif
<div class="horizontal-rule"></div>
</div>
@ -43,10 +43,10 @@
<div class="info">
<div class="product-name">
{{ $item->product->name }}
@if (isset($item->additional['attributes']))
<div class="item-options">
@foreach ($item->additional['attributes'] as $attribute)
<b>{{ $attribute['attribute_name'] }} : </b>{{ $attribute['option_label'] }}</br>
@endforeach
@ -76,6 +76,10 @@
<div class="horizontal-rule mb-10 mt-10"></div>
@endforeach
<div class="bottom-toolbar">
{{ $items->links() }}
</div>
@else
<div class="empty">
{{ __('customer::app.wishlist.empty') }}

View File

@ -32,7 +32,7 @@
@if ($product->getTypeInstance()->haveSpecialPrice())
<span class="pro-price">{{ core()->currency($product->getTypeInstance()->getSpecialPrice()) }}</span>
@else
<span class="pro-price">{{ core()->currency($product->price) }}</span>
<span class="pro-price">{{ core()->currency($product->getTypeInstance()->getMinimalPrice()) }}</span>
@endif
</div>
</div>

View File

@ -1,4 +1,4 @@
@if ($product->type == 'configurable')
@if (Webkul\Product\Helpers\ProductType::hasVariants($product->type))
@inject ('configurableOptionHelper', 'Webkul\Product\Helpers\ConfigurableOption')

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"/js/ui.js": "/js/ui.js?id=62a1f3ccf04e55a10ae8",
"/css/ui.css": "/css/ui.css?id=dec1ede3219df6514109"
"/js/ui.js": "/js/ui.js?id=96cb056c865e3f06e093",
"/css/ui.css": "/css/ui.css?id=cf2acf741bc8b8928209"
}

View File

@ -142,8 +142,6 @@ abstract class DataGrid
public function __construct()
{
$this->invoker = $this;
$this->itemsPerPage = core()->getConfigData('general.general.locale_options.admin_page_limit') ?: $this->itemsPerPage;
}
/**
@ -154,6 +152,12 @@ abstract class DataGrid
$parsedUrl = [];
$unparsed = url()->full();
$route = request()->route() ? request()->route()->getName() : "";
if ($route == 'admin.datagrid.export') {
$unparsed = url()->previous();
}
if (count(explode('?', $unparsed)) > 1) {
$to_be_parsed = explode('?', $unparsed)[1];
@ -161,6 +165,10 @@ abstract class DataGrid
unset($parsedUrl['page']);
}
$this->itemsPerPage = isset($parsedUrl['perPage']) ? $parsedUrl['perPage']['eq'] : $this->itemsPerPage;
unset($parsedUrl['perPage']);
return $parsedUrl;
}

View File

@ -1,14 +1,21 @@
<script>
export default {
bind(el, binding, vnode) {
let handler = function(e) {
setTimeout(function() {
e.target.value = e.target.value.toString().toLowerCase()
.replace(/[^\w- ]+/g,'')
.trim()
.replace(/ +/g,'-');
let handler = function (e) {
setTimeout(function () {
e.target.value = e.target.value
.toString()
.toLowerCase()
.replace(/[^\w- ]+/g, '')
// replace whitespaces with dashes
.replace(/ +/g, '-')
// avoid having multiple dashes (---- translates into -)
.replace('![-\s]+!u', '-')
.trim();
}, 100);
}
};
el.addEventListener('input', handler);
}

View File

@ -16,6 +16,17 @@
position: absolute;
right: 25px;
}
.per-page {
right: 250px;
.per-page-label {
position: absolute;
right: 120px;
width: 100%;
top: 8px;
}
}
}
.filter-row-two {

View File

@ -13,6 +13,7 @@ return [
'no-records' => 'لا توجد سجلات',
'filter-fields-missing' => 'بعض الحقل المطلوب هو لاغ ، رجاء تفقد عمود ، حالة و قيمة صحيح',
'click_on_action' => 'هل تريد حقا أن تؤدي هذا العمل؟'
'click_on_action' => 'هل تريد حقا أن تؤدي هذا العمل؟',
'items-per-page' => 'Items Per Page',
]
];

View File

@ -35,6 +35,7 @@ return [
'true' => 'True / Active',
'false' => 'False / Inactive',
'between' => 'Is between',
'apply' => 'Apply'
'apply' => 'Apply',
'items-per-page' => 'Items Per Page',
]
];

View File

@ -31,6 +31,7 @@ return [
'true' => 'صحیح / فعال',
'false' => 'غلط / غیرفعال',
'between' => 'ما بین',
'apply' => 'درخواست'
'apply' => 'درخواست',
'items-per-page' => 'Items Per Page',
]
];

View File

@ -31,6 +31,7 @@ return [
'true' => 'Verdadeiro / Ativo',
'false' => 'Falso / Inativo',
'between' => 'Está entre',
'apply' => 'Aplicar'
'apply' => 'Aplicar',
'items-per-page' => 'Items Per Page',
]
];

View File

@ -130,6 +130,22 @@
</ul>
</div>
</div>
<div class="dropdown-filters per-page">
<div class="control-group">
<label class="per-page-label" for="perPage">
{{ __('ui::app.datagrid.items-per-page') }}
</label>
<select id="perPage" name="perPage" class="control" v-model="perPage" v-on:change="paginate">
<option value="10"> 10 </option>
<option value="20"> 20 </option>
<option value="30"> 30 </option>
<option value="40"> 40 </option>
<option value="50"> 50 </option>
</select>
</div>
</div>
</div>
<div class="filter-row-two">
@ -198,12 +214,21 @@
stringConditionSelect: false,
booleanConditionSelect: false,
numberConditionSelect: false,
datetimeConditionSelect: false
datetimeConditionSelect: false,
perPage: 10,
}
},
mounted: function() {
this.setParamsAndUrl();
if (this.filters.length) {
for (let i = 0; i < this.filters.length; i++) {
if (this.filters[i].column == 'perPage') {
this.perPage = this.filters[i].val;
}
}
}
},
methods: {
@ -513,6 +538,14 @@
newParams = '';
for(i = 0; i < this.filters.length; i++) {
if (this.filters[i].column == 'status') {
if (this.filters[i].val.includes("True")) {
this.filters[i].val = 1;
} else if (this.filters[i].val.includes("False")) {
this.filters[i].val = 0;
}
}
if (i == 0) {
newParams = '?' + this.filters[i].column + '[' + this.filters[i].cond + ']' + '=' + this.filters[i].val;
} else {
@ -601,10 +634,12 @@
select: function() {
this.allSelected = false;
if(this.dataIds.length == 0)
if (this.dataIds.length == 0) {
this.massActionsToggle = false;
else
this.massActionType = null;
} else {
this.massActionsToggle = true;
}
},
//triggered when master checkbox is clicked
@ -672,6 +707,20 @@
this.massActionsToggle = false;
this.allSelected = false;
this.massActionType = null;
},
paginate: function(e) {
for (let i = 0; i < this.filters.length; i++) {
if (this.filters[i].column == 'perPage') {
this.filters.splice(i, 1);
}
}
this.filters.push({"column":"perPage","cond":"eq","val": e.target.value});
this.makeURL();
}
}
});

0
public/index.php Executable file → Normal file
View File

View File

@ -46,7 +46,7 @@
// reading env content
$data = file($envFile);
$databaseArray = ['DB_HOST', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD', 'DB_CONNECTION','DB_PORT'];
$databaseArray = ['DB_HOST', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD', 'DB_CONNECTION','DB_PORT', 'DB_PREFIX'];
$key = $value = [];
if ($data) {
@ -89,7 +89,8 @@
if (!$conn->connect_error) {
// retrieving admin entry
$sql = "SELECT id, name FROM admins";
$prefix = $databaseData['DB_PREFIX'].'admins';
$sql = "SELECT id, name FROM $prefix";
$result = $conn->query($sql);
if ($result) {

View File

@ -12,7 +12,7 @@
// reading env content
$data = file($envFile);
$databaseArray = ['DB_HOST', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD', 'DB_CONNECTION','DB_PORT'];
$databaseArray = ['DB_HOST', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD', 'DB_CONNECTION','DB_PORT', 'DB_PREFIX'];
$key = $value = [];
if ($data) {
@ -55,7 +55,8 @@
if (! $conn->connect_error) {
// retrieving admin entry
$sql = "SELECT id, name FROM admins";
$prefix = $databaseData['DB_PREFIX'].'admins';
$sql = "SELECT id, name FROM $prefix";
$result = $conn->query($sql);
if ($result) {

View File

@ -1,32 +0,0 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ExampleTest extends DuskTestCase
{
/**
* A basic browser test example.
*
* @return void
*/
public function testBasicExample()
{
$customer = app('Webkul\Customer\Repositories\CustomerRepository');
$customer = $customer->all();
$customer = $customer->first();
$this->browse(function (Browser $browser) use($customer) {
$browser->visit('/customer/login')
->type('email', $customer->email)
->type('password', $customer->password)
->click('input[type="submit"]')
->screenshot('error');
});
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class HomePage extends Page
{
/**
* Get the URL for the page.
*
* @return string
*/
public function url()
{
return '/';
}
/**
* Assert that the browser is on the page.
*
* @param Browser $browser
* @return void
*/
public function assert(Browser $browser)
{
//
}
/**
* Get the element shortcuts for the page.
*
* @return array
*/
public function elements()
{
return [
'@element' => '#selector',
];
}
}

View File

@ -1,20 +0,0 @@
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Page as BasePage;
abstract class Page extends BasePage
{
/**
* Get the global element shortcuts for the site.
*
* @return array
*/
public static function siteElements()
{
return [
'@element' => '#selector',
];
}
}

View File

@ -1,49 +0,0 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ProductCategoryTest extends DuskTestCase
{
/**
* A basic browser test example.
*
* @return void
*/
public function testGuestAddToCart()
{
$categories = app('Webkul\Category\Repositories\CategoryRepository')->all();
$products = app('Webkul\Product\Repositories\ProductRepository')->all();
$slugs = array();
foreach ($categories as $category) {
if ($category->slug != 'root') {
array_push($slugs, $category->slug);
}
}
$slugIndex = array_rand($slugs);
$testSlug = $slugs[$slugIndex];
$testProduct = array();
foreach ($products as $product) {
$categories = $product->categories;
if ($categories->last()->slug == $testSlug) {
array_push($testProduct, ['name' => $product->name, 'url_key' => $product->url_key]);
break;
}
}
$this->browse(function (Browser $browser) use($testSlug, $testProduct) {
$browser->visit(route('shop.productOrCategory.index', $testSlug));
$browser->assertSeeLink($testProduct[0]['name']);
$browser->pause(5000);
});
}
}

View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

@ -1,22 +0,0 @@
<?php
namespace Tests;
use Illuminate\Contracts\Console\Kernel;
trait CreatesApplication
{
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
return $app;
}
}

View File

@ -1,48 +0,0 @@
<?php
namespace Tests;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
static::startChromeDriver();
}
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--headless',
'--no-sandbox'
]);
return RemoteWebDriver::create(
'http://localhost:9515/', DesiredCapabilities::chrome()
);
// return RemoteWebDriver::create(
// 'http://localhost:9515', DesiredCapabilities::chrome()->setCapability(
// ChromeOptions::CAPABILITY, $options
// )
// );
}
}

View File

@ -1,124 +0,0 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Auth;
use Crypt;
use App;
use Faker\Generator as Faker;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Webkul\Customer\Repositories\CustomerRepository as Customer;
class AuthTest extends TestCase
{
protected $customer;
/**
* To check if the customer can view the login page or not
*
* @return void
*/
public function testCustomerLoginPage()
{
config(['app.url' => 'http://prashant.com']);
$response = $this->get('/customer/login');
$response->assertSuccessful();
$response->assertViewIs('shop::customers.session.index');
}
public function testCustomerResgistrationPage()
{
config(['app.url' => 'http://prashant.com']);
$response = $this->get('/customer/register');
$response->assertSuccessful();
$response->assertViewIs('shop::customers.signup.index');
}
public function testCustomerRegistration() {
$faker = \Faker\Factory::create();
$allCustomers = array();
$customers = app(Customer::class);
$created = $customers->create([
'first_name' => explode(' ', $faker->name)[0],
'last_name' => explode(' ', $faker->name)[0],
'channel_id' => core()->getCurrentChannel()->id,
'gender' => $faker->randomElement($array = array ('Male','Female', 'Other')),
'date_of_birth' => $faker->date($format = 'Y-m-d', $max = 'now'),
'email' => $faker->email,
'password' => bcrypt('12345678'),
'is_verified' => 1
]);
$this->assertEquals($created->id, $created->id);
}
public function testCustomerLogin()
{
config(['app.url' => 'http://prashant.com']);
$customers = app(Customer::class);
$customer = $customers->findOneByField('email', 'john@doe.net');
$response = $this->post('/customer/login', [
'email' => $customer->email,
'password' => '12345678'
]);
$response->assertRedirect('/customer/account/profile');
}
/**
* Test that customer cannot login with the wrong credentials.
*/
public function willNotLoginWithWrongCredentials()
{
$customers = app(Customer::class);
$customer = $customers->findOneByField('email', 'john@doe.net');
$response = $this->from(route('login'))->post(route('customer.session.create'),
[
'email' => $customer->email,
'password' => 'wrongpassword3428903mlndvsnljkvsd',
]);
$this->assertGuest();
}
/**
* Test to confirm that customer cannot login if user does not exist.
*/
public function willNotLoginWithNonexistingCustomer()
{
$response = $this->post(route('customer.session.create'), [
'email' => 'fiaiia9q2943jklq34h203qtb3o2@something.com',
'password' => 'wrong-password',
]);
$this->assertGuest();
}
/**
* To test that customer can logout
*/
public function allowsCustomerToLogout()
{
$customer = auth()->guard('customer')->user();
$this->get(route('customer.session.destroy'));
$this->assertGuest();
}
}

View File

@ -1,88 +0,0 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Http\Request;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Webkul\Category\Repositories\CategoryRepository;
class GeneralTest extends TestCase
{
/**
* Test for home page
*
* @return void
*/
public function testHomePage()
{
config(['app.url' => 'http://prashant.com']);
$response = $this->get('/');
$response->assertStatus(200);
}
/**
* Test for customer login
*
* @return void
*/
public function testCustomerLoginPage()
{
config(['app.url' => 'http://prashant.com']);
$response = $this->get('/customer/login');
$response->assertStatus(200);
}
/**
* Test for categories page
*
* @return void
*/
public function testCategoriesPage()
{
$categoryUrlSlug = 'marvel-figurines';
config(['app.url' => 'http://prashant.com']);
$response = $this->get("/categories/{$categoryUrlSlug}");
$response->assertStatus(200);
}
/**
* Test for customer registration page
*
* @return void
*/
public function testCustomerRegistrationPage()
{
// config(['app.url' => 'http://127.0.0.1:8000']);
$response = $this->get("/customer/register");
$response->assertStatus(200);
}
/**
* Test for checkout's cart page
*
* @return void
*/
public function testCartPage()
{
// config(['app.url' => 'http://127.0.0.1:8000']);
$response = $this->get("/checkout/cart");
$response->assertStatus(200);
}
}

View File

@ -1,10 +0,0 @@
<?php
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
}

View File

@ -1,19 +0,0 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$this->assertTrue(true);
}
}

0
tests/_data/.gitkeep Normal file
View File

2
tests/_output/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1,26 @@
<?php
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void pause()
*
* @SuppressWarnings(PHPMD)
*/
class AcceptanceTester extends \Codeception\Actor
{
use _generated\AcceptanceTesterActions;
/**
* Define custom actions here
*/
}

View File

@ -0,0 +1,61 @@
<?php
use Illuminate\Routing\RouteCollection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Webkul\User\Models\Admin;
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void pause()
*
* @SuppressWarnings(PHPMD)
*/
class FunctionalTester extends \Codeception\Actor
{
use _generated\FunctionalTesterActions;
/**
* Define custom actions here
*/
/**
* Login as default administrator
*/
public function loginAsAdmin(): void
{
$I = $this;
Auth::guard('admin')->login($I->grabRecord(Admin::class, ['email' => 'admin@example.com']));
$I->seeAuthentication('admin');
}
/**
* Go to a specific route and check if admin guard is applied on it
*
* @param string $name name of the route
* @param array|null $params params the route will be created with
*/
public function amOnAdminRoute(string $name, array $params = null): void
{
$I = $this;
$I->amOnRoute($name, $params);
$I->seeCurrentRouteIs($name);
/** @var RouteCollection $routes */
$routes = Route::getRoutes();
$middlewares = $routes->getByName($name)->middleware();
$I->assertContains('admin', $middlewares, 'check that admin middleware is applied');
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class Acceptance extends \Codeception\Module
{
}

View File

@ -0,0 +1,10 @@
<?php
namespace Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class Functional extends \Codeception\Module
{
}

View File

@ -0,0 +1,10 @@
<?php
namespace Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class Trigger extends \Codeception\Module
{
}

View File

@ -0,0 +1,10 @@
<?php
namespace Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
class Unit extends \Codeception\Module
{
}

View File

@ -0,0 +1,26 @@
<?php
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void pause()
*
* @SuppressWarnings(PHPMD)
*/
class TriggerTester extends \Codeception\Actor
{
use _generated\TriggerTesterActions;
/**
* Define custom actions here
*/
}

View File

@ -0,0 +1,26 @@
<?php
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method void pause()
*
* @SuppressWarnings(PHPMD)
*/
class UnitTester extends \Codeception\Actor
{
use _generated\UnitTesterActions;
/**
* Define custom actions here
*/
}

2
tests/_support/_generated/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1,13 @@
# Codeception Test Suite Configuration
#
# Suite for acceptance tests.
# Perform tests in browser using the WebDriver or PhpBrowser.
# If you need both WebDriver and PHPBrowser tests - create a separate suite.
actor: AcceptanceTester
modules:
enabled:
- PhpBrowser:
url: http://localhost
- \Helper\Acceptance
step_decorators: ~

View File

@ -0,0 +1,20 @@
# Codeception Test Suite Configuration
#
# Suite for functional tests
# Emulate web requests and make application process them
# Include one of framework modules (Symfony2, Yii2, Laravel5) to use it
# Remove this suite if you don't use frameworks
actor: FunctionalTester
modules:
enabled:
# add a framework module here
- \Helper\Functional
- Asserts
- Laravel5:
environment_file: .env.testing
run_database_migrations: true
run_database_seeder: true
database_seeder_class: DatabaseSeeder
step_decorators: ~

View File

@ -0,0 +1,70 @@
<?php
namespace Tests\Functional\Shop;
use Faker\Factory;
use Faker\Generator;
use FunctionalTester;
use Webkul\Product\Models\Product;
use Webkul\Product\Models\ProductFlat;
class ProductControllerCest
{
/** @var Generator */
private $faker;
public function _before(FunctionalTester $I)
{
$this->faker = Factory::create();
}
public function testCreate(FunctionalTester $I)
{
$I->loginAsAdmin();
$I->amOnAdminRoute('admin.catalog.products.index');
$I->click(__('admin::app.catalog.products.add-product-btn-title'), '//*[contains(@class, "page-action")]');
$I->seeCurrentRouteIs('admin.catalog.products.create');
$I->click(__('admin::app.catalog.products.save-btn-title'), '//*[contains(@class, "page-action")]');
$I->seeFormHasErrors();
$testSku = $this->faker->uuid;
$I->selectOption('//select[@id="attribute_family_id"]', 'Default');
$I->fillField('//input[@id="sku"]', $testSku);
$I->click(__('admin::app.catalog.products.save-btn-title'), '//*[contains(@class, "page-action")]');
$I->dontSeeFormErrors();
$I->seeCurrentRouteIs('admin.catalog.products.edit');
$I->seeRecord(Product::class, ['sku' => $testSku]);
$I->click(__('admin::app.catalog.products.save-btn-title'), '//*[contains(@class, "page-action")]');
$I->seeFormHasErrors();
$testName = $this->faker->name;
$testUrlKey = $testName;
$testDescription = $this->faker->sentence;
$testDescriptionShop = $this->faker->sentence;
$testPrice = $this->faker->randomFloat(2, 1, 100);
$testWeight = $this->faker->numberBetween(1, 20);
$I->fillField('//input[@id="name"]', $testName);
$I->fillField('//input[@id="url_key"]', $testUrlKey);
$I->fillField('//textarea[@id="description"]', $testDescription);
$I->fillField('//textarea[@id="short_description"]', $testDescriptionShop);
$I->fillField('//input[@id="price"]', $testPrice);
$I->fillField('//input[@id="weight"]', $testWeight);
$I->click(__('admin::app.catalog.products.save-btn-title'), '//*[contains(@class, "page-action")]');
$I->dontSeeFormErrors();
$I->seeCurrentRouteIs('admin.catalog.products.index');
$product = $I->grabRecord(Product::class, ['sku' => $testSku]);
$I->seeRecord(ProductFlat::class, [
'sku' => $testSku,
'name' => $testName,
'description' => $testDescription,
'short_description' => $testDescriptionShop,
'url_key' => $testUrlKey,
'price' => $testPrice,
'weight' => $testWeight,
'product_id' => $product->id,
]);
}
}

18
tests/trigger.suite.yml Normal file
View File

@ -0,0 +1,18 @@
# Codeception Test Suite Configuration
#
# Suite for unit or integration tests that test database logic (e.g. triggers).
actor: TriggerTester
modules:
enabled:
- Asserts
- Filesystem
- \Helper\Unit
- Laravel5:
environment_file: .env.testing
run_database_migrations: true
run_database_seeder: true
database_seeder_class: DatabaseSeeder
packages: packages
step_decorators: ~

View File

@ -0,0 +1,101 @@
<?php
namespace Tests\Unit\Shop;
use UnitTester;
use Webkul\Category\Models\Category;
use Faker\Factory;
use Illuminate\Support\Facades\DB;
use Webkul\Core\Models\Locale;
class DatabaseLogicCest
{
private $faker;
/** @var Locale $localeEn */
private $localeEn;
/** @var Locale $localeDe */
private $localeDe;
public function _before(UnitTester $I)
{
$this->faker = Factory::create();
$this->localeEn = $I->grabRecord(Locale::class, [
'code' => 'en',
]);
$this->localeDe = $I->have(Locale::class, [
'code' => 'de',
'name' => 'German',
]);
$I->assertNotNull($this->localeDe);
}
public function testGetUrlPathOfCategory(UnitTester $I)
{
$parentCategoryName = $this->faker->word;
$parentCategoryAttributes = [
'parent_id' => 1,
'position' => 1,
'status' => 1,
$this->localeEn->code => [
'name' => $parentCategoryName,
'slug' => strtolower($parentCategoryName),
'description' => $parentCategoryName,
'locale_id' => $this->localeEn->id,
],
$this->localeDe->code => [
'name' => $parentCategoryName,
'slug' => strtolower($parentCategoryName),
'description' => $parentCategoryName,
'locale_id' => $this->localeDe->id,
],
];
$parentCategory = $I->have(Category::class, $parentCategoryAttributes);
$I->assertNotNull($parentCategory);
$categoryName = $this->faker->word;
$categoryAttributes = [
'position' => 1,
'status' => 1,
'parent_id' => $parentCategory->id,
$this->localeEn->code => [
'name' => $categoryName,
'slug' => strtolower($categoryName),
'description' => $categoryName,
'locale_id' => $this->localeEn->id,
],
$this->localeDe->code => [
'name' => $categoryName,
'slug' => strtolower($categoryName),
'description' => $categoryName,
'locale_id' => $this->localeDe->id,
],
];
$category = $I->have(Category::class, $categoryAttributes);
$I->assertNotNull($category);
$sqlStoredFunction = 'SELECT get_url_path_of_category(:category_id, :locale_code) AS url_path;';
$urlPathQueryResult = DB::selectOne($sqlStoredFunction, [
'category_id' => $parentCategory->id,
'locale_code' => $this->localeEn->code,
]);
$I->assertNotNull($urlPathQueryResult->url_path);
$I->assertEquals(strtolower($parentCategoryName), $urlPathQueryResult->url_path);
$urlPathQueryResult = DB::selectOne($sqlStoredFunction, [
'category_id' => $category->id,
'locale_code' => $this->localeEn->code,
]);
$I->assertNotNull($urlPathQueryResult->url_path);
$expectedUrlPath = strtolower($parentCategoryName) . '/' . strtolower($categoryName);
$I->assertEquals($expectedUrlPath, $urlPathQueryResult->url_path);
}
}

View File

@ -0,0 +1,222 @@
<?php
namespace Tests\Unit\Shop;
use Faker\Factory;
use UnitTester;
use Webkul\Category\Models\Category;
use Webkul\Category\Models\CategoryTranslation;
use Webkul\Core\Models\Locale;
class TriggerCest
{
private $faker;
private $parentCategory;
private $category;
private $parentCategoryAttributes;
private $categoryAttributes;
private $parentCategoryName;
private $categoryName;
/** @var Locale $localeEn */
private $localeEn;
/** @var Locale $localeDe */
private $localeDe;
public function _before(UnitTester $I)
{
$this->faker = Factory::create();
$this->parentCategoryName = $this->faker->word;
$this->categoryName = $this->faker->word . $this->faker->randomDigit;
$this->localeEn = $I->grabRecord(Locale::class, [
'code' => 'en',
]);
$this->localeDe = $I->have(Locale::class, [
'code' => 'de',
'name' => 'German',
]);
$this->parentCategoryAttributes = [
'parent_id' => 1,
'position' => 1,
'status' => 1,
$this->localeEn->code => [
'name' => $this->parentCategoryName,
'slug' => strtolower($this->parentCategoryName),
'description' => $this->parentCategoryName,
'locale_id' => $this->localeEn->id,
],
$this->localeDe->code => [
'name' => $this->parentCategoryName,
'slug' => strtolower($this->parentCategoryName),
'description' => $this->parentCategoryName,
'locale_id' => $this->localeDe->id,
],
];
$this->parentCategory = $I->have(Category::class, $this->parentCategoryAttributes);
$I->assertNotNull($this->parentCategory);
$this->categoryAttributes = [
'position' => 1,
'status' => 1,
'parent_id' => $this->parentCategory->id,
$this->localeEn->code => [
'name' => $this->categoryName,
'slug' => strtolower($this->categoryName),
'description' => $this->categoryName,
'locale_id' => $this->localeEn->id,
],
$this->localeDe->code => [
'name' => $this->categoryName,
'slug' => strtolower($this->categoryName),
'description' => $this->categoryName,
'locale_id' => $this->localeDe->id,
],
];
$this->category = $I->have(Category::class, $this->categoryAttributes);
$I->assertNotNull($this->category);
}
public function testInsertTriggerOnCategoryTranslationsTable(UnitTester $I)
{
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->parentCategory->id,
'name' => $this->parentCategoryName,
'locale' => $this->localeEn->code,
'url_path' => strtolower($this->parentCategoryName)
]);
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->parentCategory->id,
'name' => $this->parentCategoryName,
'locale' => $this->localeDe->code,
'url_path' => strtolower($this->parentCategoryName)
]);
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->category->id,
'name' => $this->categoryName,
'locale' => $this->localeEn->code,
'url_path' => strtolower($this->parentCategoryName) . '/' . strtolower($this->categoryName)
]);
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->category->id,
'name' => $this->categoryName,
'locale' => $this->localeDe->code,
'url_path' => strtolower($this->parentCategoryName) . '/' . strtolower($this->categoryName)
]);
}
public function testUpdateTriggersOnCategoryTranslationsTable(UnitTester $I)
{
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->category->id,
'name' => $this->categoryName,
'slug' => strtolower($this->categoryName),
'locale' => $this->localeEn->code,
'url_path' => strtolower($this->parentCategoryName) . '/' . strtolower($this->categoryName),
]);
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->category->id,
'name' => $this->categoryName,
'slug' => strtolower($this->categoryName),
'locale' => $this->localeDe->code,
'url_path' => strtolower($this->parentCategoryName) . '/' . strtolower($this->categoryName),
]);
$newCategoryName = $this->faker->word;
$this->categoryAttributes[$this->localeDe->code]['name'] = $newCategoryName;
$this->categoryAttributes[$this->localeDe->code]['slug'] = strtolower($newCategoryName);
$I->assertTrue($this->category->update($this->categoryAttributes));
$this->category->refresh();
$I->dontSeeRecord(CategoryTranslation::class, [
'category_id' => $this->category->id,
'name' => $newCategoryName,
'slug' => strtolower($this->categoryName),
'locale' => $this->localeEn->code,
'url_path' => strtolower($this->parentCategoryName) . '/' . strtolower($this->categoryName),
]);
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->category->id,
'name' => $newCategoryName,
'slug' => strtolower($newCategoryName),
'locale' => $this->localeDe->code,
'url_path' => strtolower($this->parentCategoryName) . '/' . strtolower($newCategoryName),
]);
}
public function testInsertTriggersOnCategoriesTable(UnitTester $I)
{
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->parentCategory->id,
'name' => $this->parentCategoryName,
'slug' => strtolower($this->parentCategoryName),
'locale' => $this->localeEn->code,
'url_path' => strtolower($this->parentCategoryName),
]);
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->category->id,
'name' => $this->categoryName,
'slug' => strtolower($this->categoryName),
'locale' => $this->localeEn->code,
'url_path' => strtolower($this->parentCategoryName) . '/' . $this->categoryName,
]);
}
public function testUpdateTriggersOnCategoriesTable(UnitTester $I)
{
$I->seeRecord(Category::class, [
'id' => $this->category->id,
'parent_id' => $this->parentCategory->id,
]);
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->category->id,
'name' => $this->categoryName,
'slug' => strtolower($this->categoryName),
'locale' => $this->localeEn->code,
'url_path' => strtolower($this->parentCategoryName) . '/' . strtolower($this->categoryName),
]);
$category2Name = $this->faker->word;
$category2Attributes = [
'position' => 1,
'status' => 1,
'parent_id' => $this->parentCategory->id,
$this->localeEn->code => [
'name' => $category2Name,
'slug' => strtolower($category2Name),
'description' => $category2Name,
'locale_id' => $this->localeEn->id,
],
];
$category2 = $I->have(Category::class, $category2Attributes);
$I->assertNotNull($category2);
$this->categoryAttributes['parent_id'] = $category2->id;
$I->assertTrue($this->category->update($this->categoryAttributes));
$this->category->refresh();
$expectedUrlPath = strtolower($this->parentCategoryName) . '/'
. strtolower($category2Name) . '/'
. strtolower($this->categoryName);
$I->seeRecord(CategoryTranslation::class, [
'category_id' => $this->category->id,
'name' => $this->categoryName,
'slug' => strtolower($this->categoryName),
'locale' => $this->localeEn->code,
'url_path' => $expectedUrlPath,
]);
}
}

18
tests/unit.suite.yml Normal file
View File

@ -0,0 +1,18 @@
# Codeception Test Suite Configuration
#
# Suite for unit or integration tests.
actor: UnitTester
modules:
enabled:
- Asserts
- Filesystem
- \Helper\Unit
- Laravel5:
environment_file: .env.testing
run_database_migrations: true
run_database_seeder: true
database_seeder_class: DatabaseSeeder
packages: packages
step_decorators: ~

Some files were not shown because too many files have changed in this diff Show More