merge master, resolve comnflicts

This commit is contained in:
Steffen Mahler 2020-06-17 08:12:10 +02:00
commit 967b0903e5
25 changed files with 294 additions and 1901 deletions

View File

@ -24,8 +24,7 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')
// ->hourly();
$schedule->command('booking:cron')->dailyAt('3:00');
}
/**
@ -36,6 +35,7 @@ class Kernel extends ConsoleKernel
protected function commands()
{
$this->load(__DIR__.'/Commands');
$this->load(__DIR__.'/../../packages/Webkul/Core/src/Console/Commands');
require base_path('routes/console.php');
}

View File

@ -2,6 +2,7 @@
return array (
'save' => 'Speichern',
'copy-of' => 'Kopie von',
'create' => 'Erstellen',
'update' => 'Update',
'delete' => 'Löschen',

View File

@ -2,6 +2,7 @@
return [
'save' => 'Save',
'copy-of' => 'Copy of',
'create' => 'Create',
'update' => 'Update',
'delete' => 'Delete',

View File

@ -309,7 +309,7 @@
@foreach (app('Webkul\Core\Repositories\LocaleRepository')->all() as $locale)
<td>
<div class="control-group" :class="[errors.has(localeInputName(row, '{{ $locale->code }}')) ? 'has-error' : '']">
<input type="text" v-validate="getOptionValidation(row, '{{ $locale->code }}')" v-model="row['{{ $locale->code }}']" :name="localeInputName(row, '{{ $locale->code }}')" class="control" data-vv-as="&quot;{{ $locale->name . ' (' . $locale->code . ')' }}&quot;"/>
<input type="text" v-validate="getOptionValidation(row, '{{ $locale->code }}')" v-model="row['locales']['{{ $locale->code }}']" :name="localeInputName(row, '{{ $locale->code }}')" class="control" data-vv-as="&quot;{{ $locale->name . ' (' . $locale->code . ')' }}&quot;"/>
<span class="control-error" v-if="errors.has(localeInputName(row, '{{ $locale->code }}'))">@{{ errors.first(localeInputName(row, '{!! $locale->code !!}')) }}</span>
</div>
</td>
@ -383,10 +383,10 @@
addOptionRow: function (isNullOptionRow) {
const rowCount = this.optionRowCount++;
const id = 'option_' + rowCount;
let row = {'id': id};
let row = {'id': id, 'locales': {}};
@foreach (app('Webkul\Core\Repositories\LocaleRepository')->all() as $locale)
row['{{ $locale->code }}'] = '';
row['locales']['{{ $locale->code }}'] = '';
@endforeach
row['notRequired'] = '';

View File

@ -379,7 +379,7 @@
@foreach (app('Webkul\Core\Repositories\LocaleRepository')->all() as $locale)
<td>
<div class="control-group" :class="[errors.has(localeInputName(row, '{{ $locale->code }}')) ? 'has-error' : '']">
<input type="text" v-validate="getOptionValidation(row, '{{ $locale->code }}')" v-model="row['{{ $locale->code }}']" :name="localeInputName(row, '{{ $locale->code }}')" class="control" data-vv-as="&quot;{{ $locale->name . ' (' . $locale->code . ')' }}&quot;"/>
<input type="text" v-validate="getOptionValidation(row, '{{ $locale->code }}')" v-model="row['locales']['{{ $locale->code }}']" :name="localeInputName(row, '{{ $locale->code }}')" class="control" data-vv-as="&quot;{{ $locale->name . ' (' . $locale->code . ')' }}&quot;"/>
<span class="control-error" v-if="errors.has(localeInputName(row, '{{ $locale->code }}'))">@{{ errors.first(localeInputName(row, '{!! $locale->code !!}')) }}</span>
</div>
</td>
@ -434,7 +434,8 @@
'sort_order': @json($option->sort_order),
'swatch_value': @json($option->swatch_value),
'swatch_value_url': @json($option->swatch_value_url),
'notRequired': ''
'notRequired': '',
'locales': {}
};
@if (empty($option->label))
@ -444,7 +445,7 @@
@endif
@foreach (app('Webkul\Core\Repositories\LocaleRepository')->all() as $locale)
row['{{ $locale->code }}'] = @json($option->translate($locale->code)['label'] ?? '');
row['locales']['{{ $locale->code }}'] = @json($option->translate($locale->code)['label'] ?? '');
@endforeach
this.optionRows.push(row);
@ -465,10 +466,10 @@
addOptionRow: function (isNullOptionRow) {
const rowCount = this.optionRowCount++;
const id = 'option_' + rowCount;
let row = {'id': id};
let row = {'id': id, 'locales': {}};
@foreach (app('Webkul\Core\Repositories\LocaleRepository')->all() as $locale)
row['{{ $locale->code }}'] = '';
row['locales']['{{ $locale->code }}'] = '';
@endforeach
row['notRequired'] = '';

View File

@ -118,10 +118,10 @@
height: 200,
width: "100%",
plugins: 'image imagetools media wordcount save fullscreen code table lists link hr',
toolbar1: 'formatselect | bold italic strikethrough forecolor backcolor alignleft aligncenter alignright alignjustif| link hr |numlist bullist outdent indent | removeformat | code | table',
toolbar1: 'formatselect | bold italic strikethrough forecolor backcolor alignleft aligncenter alignright alignjustify | link hr |numlist bullist outdent indent | removeformat | code | table',
image_advtab: true,
valid_elements : '*[*]'
});
});
</script>
@endpush
@endpush

View File

@ -21,7 +21,7 @@ mix.js(__dirname + "/src/Resources/assets/js/app.js", "js/admin.js")
processCssUrls: false
});
if (!mix.inProduction()) {
if (! mix.inProduction()) {
mix.sourceMaps();
}

View File

@ -0,0 +1,19 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Carbon\Carbon;
use Faker\Generator as Faker;
use Webkul\BookingProduct\Models\BookingProduct;
use Webkul\BookingProduct\Models\BookingProductEventTicket;
use Webkul\Product\Models\Product;
$factory->define(BookingProductEventTicket::class, function (Faker $faker, array $attributes) {
return [
'price' => $faker->randomFloat(4, 3, 900),
'qty' => $faker->randomNumber(2),
'booking_product_id' => static function () {
return factory(BookingProduct::class)->create(['type' => 'event'])->id;
}
];
});

View File

@ -0,0 +1,20 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Carbon\Carbon;
use Faker\Generator as Faker;
use Webkul\BookingProduct\Models\BookingProduct;
use Webkul\Product\Models\Product;
$factory->define(BookingProduct::class, function (Faker $faker, array $attributes) {
return [
'type' => array_rand(['event']),
'qty' => $faker->randomNumber(2),
'available_from' => Carbon::yesterday(),
'available_to' => Carbon::tomorrow(),
'product_id' => function () {
return factory(Product::class)->create(['type' => 'booking'])->id;
}
];
});

View File

@ -2,6 +2,7 @@
namespace Webkul\BookingProduct\Providers;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
class BookingProductServiceProvider extends ServiceProvider
@ -10,8 +11,9 @@ class BookingProductServiceProvider extends ServiceProvider
* Bootstrap services.
*
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function boot()
public function boot(): void
{
$this->loadRoutesFrom(__DIR__ . '/../Http/front-routes.php');
@ -26,6 +28,8 @@ class BookingProductServiceProvider extends ServiceProvider
], 'public');
$this->app->register(EventServiceProvider::class);
$this->app->make(EloquentFactory::class)->load(__DIR__ . '/../Database/Factories');
}
/**
@ -33,7 +37,7 @@ class BookingProductServiceProvider extends ServiceProvider
*
* @return void
*/
public function register()
public function register(): void
{
$this->mergeConfigFrom(
dirname(__DIR__) . '/Config/product_types.php', 'product_types'

View File

@ -85,10 +85,21 @@ class CartRuleController extends Controller
$copiedCartRule = $originalCartRule
->replicate()
->fill(['status' => 0]);
->fill([
'status' => 0,
'name' => __('admin::app.copy-of') . ' ' . $originalCartRule->name,
]);
$copiedCartRule->save();
foreach ($copiedCartRule->channels as $channel) {
$copiedCartRule->channels()->save($channel);
}
foreach ($copiedCartRule->customer_groups as $group) {
$copiedCartRule->customer_groups()->save($group);
}
return view($this->_config['view'], [
'cartRule' => $copiedCartRule,
]);

View File

@ -0,0 +1,75 @@
<?php
namespace Webkul\Core\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Webkul\Attribute\Models\Attribute;
use Webkul\BookingProduct\Models\BookingProduct;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Product\Models\ProductFlat;
class BookingCron extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'booking:cron';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Deactivates all expired Booking Products of type event';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$expiredEvents = BookingProduct::query()
->join('product_flat', 'booking_products.product_id', '=', 'product_flat.product_id')
->where('booking_products.type', 'event')
->where('booking_products.available_to', '<=', Carbon::now())
->where('product_flat.status', 1)
->get();
if (count($expiredEvents) > 0) {
$attStatusId = Attribute::query()->select('id')
->where('code', 'status')
->first()
->id;
foreach ($expiredEvents as $expEvent) {
ProductAttributeValue::query()->where('product_id', $expEvent->product_id)
->where('attribute_id', $attStatusId)
->update(['boolean_value' => 0]);
ProductFlat::query()->where('product_id', $expEvent->product_id)
->update(['status' => 0]);
Log::info('BookingCron: deactivated expired event', ['booking_product_id' => $expEvent->id, 'product_id' => $expEvent->product_id]);
}
$this->info('All expired events have been deactivated');
} else {
Log::info('BookingCron: Did not find any expired events to be deactivated');
$this->info('Did not find any expired events to be deactivated');
}
}
}

View File

@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\AliasLoader;
use Webkul\Core\Console\Commands\BookingCron;
use Webkul\Core\Core;
use Webkul\Core\Exceptions\Handler;
use Webkul\Core\Facades\Core as CoreFacade;
@ -22,6 +23,7 @@ class CoreServiceProvider extends ServiceProvider
* Bootstrap services.
*
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function boot()
{
@ -86,7 +88,12 @@ class CoreServiceProvider extends ServiceProvider
protected function registerCommands(): void
{
if ($this->app->runningInConsole()) {
$this->commands([BagistoVersion::class, Install::class, ExchangeRateUpdate::class]);
$this->commands([
BagistoVersion::class,
Install::class,
ExchangeRateUpdate::class,
BookingCron::class
]);
}
}
@ -96,6 +103,7 @@ class CoreServiceProvider extends ServiceProvider
* @param string $path
*
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function registerEloquentFactoriesFrom($path): void
{

View File

@ -22,7 +22,7 @@ class CreateProductCustomerGroupPricesTable extends Migration
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->integer('customer_group_id')->nullble()->unsigned();
$table->integer('customer_group_id')->unsigned()->nullable();
$table->foreign('customer_group_id')->references('id')->on('customer_groups')->onDelete('cascade');
$table->timestamps();
});

View File

@ -36,7 +36,13 @@ class BundleOption extends AbstractProduct
$options = [];
foreach ($this->product->bundle_options as $option) {
$options[$option->id] = $this->getOptionItemData($option);
$data = $this->getOptionItemData($option);
if (! count($data['products'])) {
continue;
}
$options[$option->id] = $data;
}
usort ($options, function($a, $b) {
@ -79,6 +85,10 @@ class BundleOption extends AbstractProduct
$products = [];
foreach ($option->bundle_option_products as $index => $bundleOptionProduct) {
if (! $bundleOptionProduct->product->getTypeInstance()->isSaleable()) {
continue;
}
$products[$bundleOptionProduct->id] = [
'id' => $bundleOptionProduct->id,
'qty' => $bundleOptionProduct->qty,
@ -110,15 +120,15 @@ class BundleOption extends AbstractProduct
{
$products = [];
$products[$product->id] = [
'id' => $product->id,
'qty' => $product->qty,
'price' => $product->product->getTypeInstance()->getProductPrices(),
'name' => $product->product->name,
'product_id' => $product->product_id,
'is_default' => $product->is_default,
'sort_order' => $product->sort_order,
];
$products[$product->id] = [
'id' => $product->id,
'qty' => $product->qty,
'price' => $product->product->getTypeInstance()->getProductPrices(),
'name' => $product->product->name,
'product_id' => $product->product_id,
'is_default' => $product->is_default,
'sort_order' => $product->sort_order,
];
usort ($products, function($a, $b) {
if ($a['sort_order'] == $b['sort_order']) {

View File

@ -18,14 +18,15 @@ class SearchRepository extends Repository
/**
* Create a new repository instance.
*
* @param Webkul\Product\Repositories\ProductRepository $productRepository
* @param \Webkul\Product\Repositories\ProductRepository $productRepository
* @param \Illuminate\Container\Container $app
*
* @return void
*/
public function __construct(
ProductRepository $productRepository,
App $app
)
{
) {
parent::__construct($app);
$this->productRepository = $productRepository;
@ -38,8 +39,6 @@ class SearchRepository extends Repository
public function search($data)
{
$products = $this->productRepository->searchProductByAttribute($data['term']);
return $products;
return $this->productRepository->searchProductByAttribute($data['term'] ?? '');
}
}

View File

@ -605,8 +605,8 @@ abstract class AbstractType
}
if ($price->value < $lastPrice) {
if ($price->value_type == 'percentage') {
$lastPrice = $product->price * ($price->value / 100);
if ($price->value_type == 'discount') {
$lastPrice = $product->price - ($product->price * $price->value) / 100;
} else {
$lastPrice = $price->value;
}

View File

@ -93,7 +93,7 @@ class Bundle extends AbstractType
ProductRepository $productRepository,
ProductAttributeValueRepository $attributeValueRepository,
ProductInventoryRepository $productInventoryRepository,
productImageRepository $productImageRepository,
ProductImageRepository $productImageRepository,
ProductBundleOptionRepository $productBundleOptionRepository,
ProductBundleOptionProductRepository $productBundleOptionProductRepository,
ProductImage $productImageHelper,
@ -234,6 +234,10 @@ class Bundle extends AbstractType
$optionPrices = [];
foreach ($option->bundle_option_products as $index => $bundleOptionProduct) {
if (! $bundleOptionProduct->product->getTypeInstance()->isSaleable()) {
continue;
}
$optionPrices[] = $bundleOptionProduct->qty
* ($minPrice
? $bundleOptionProduct->product->getTypeInstance()->getMinimalPrice()
@ -271,6 +275,10 @@ class Bundle extends AbstractType
foreach ($this->product->bundle_options as $option) {
foreach ($option->bundle_option_products as $index => $bundleOptionProduct) {
if (! $bundleOptionProduct->product->getTypeInstance()->isSaleable()) {
continue;
}
if (in_array($option->type, ['multiselect', 'checkbox'])) {
if (! isset($optionPrices[$option->id][0])) {
$optionPrices[$option->id][0] = 0;
@ -304,6 +312,10 @@ class Bundle extends AbstractType
foreach ($this->product->bundle_options as $option) {
foreach ($option->bundle_option_products as $index => $bundleOptionProduct) {
if (! $bundleOptionProduct->product->getTypeInstance()->isSaleable()) {
continue;
}
if (in_array($option->type, ['multiselect', 'checkbox'])) {
if (! isset($optionPrices[$option->id][0])) {
$optionPrices[$option->id][0] = 0;
@ -427,6 +439,10 @@ class Bundle extends AbstractType
foreach ($this->getCartChildProducts($data) as $productId => $data) {
$product = $this->productRepository->find($productId);
if (! $product->getTypeInstance()->isSaleable()) {
continue;
}
$cartProduct = $product->getTypeInstance()->prepareForCart(array_merge($data, ['parent_id' => $this->product->id]));
if (is_string($cartProduct)) {
@ -473,6 +489,10 @@ class Bundle extends AbstractType
'product_bundle_option_id' => $optionId,
]);
if (! $optionProduct->product->getTypeInstance()->isSaleable()) {
continue;
}
$qty = $data['bundle_option_qty'][$optionId] ?? $optionProduct->qty;
if (! isset($products[$optionProduct->product_id])) {

File diff suppressed because one or more lines are too long

View File

@ -105,7 +105,7 @@ class ProductRepository extends Repository
*/
public function searchProductsFromCategory($params)
{
$term = $params['term'];
$term = $params['term'] ?? '';
$categoryId = $params['category'];
$results = app(ProductFlatRepository::class)->scopeQuery(function($query) use($term, $categoryId, $params) {

View File

@ -2,4 +2,4 @@
@section('title', __('Service Unavailable'))
@section('code', '503')
@section('message', __($exception->getMessage() ?: 'Service Unavailable'))
@section('message', __($exception->getMessage() ?: 'Srvice Unavailable'))

View File

@ -15,7 +15,7 @@ class DataMocker extends Module
/**
* Get an instance of the faker
*
* @return Generator
* @return \Faker\Generator
*
* @author florianbosdorff
*/

View File

@ -3,6 +3,7 @@
namespace Tests\Functional\CartRule;
use FunctionalTester;
use Illuminate\Support\Facades\DB;
use Webkul\CartRule\Models\CartRule;
class CartRuleCopyCest
@ -16,19 +17,44 @@ class CartRuleCopyCest
'status' => 1,
]);
$count = count(cartRule::all());
DB::table('cart_rule_channels')->insert([
'cart_rule_id' => $original->id,
'channel_id' => 1,
]);
DB::table('cart_rule_customer_groups')->insert([
'cart_rule_id' => $original->id,
'customer_group_id' => 1,
]);
$count = count(CartRule::all());
$I->amOnAdminRoute('admin.cart-rules.copy', ['id' => $original->id]);
$I->seeRecord(CartRule::class, [
'id' => $original->id + 1,
'status' => 0,
'name' => $original->name,
'name' => 'Copy of ' . $original->name,
]);
$I->assertCount($count + 1, CartRule::all());
$I->assertEquals(
DB::table('cart_rule_channels')
->pluck('cart_rule_id', 'channel_id')
->toArray(),
[1 => $original->id + 1]
);
$I->assertEquals(
DB::table('cart_rule_customer_groups')
->pluck('cart_rule_id', 'customer_group_id')
->toArray(),
[1 => $original->id + 1]
);
$I->seeResponseCodeIsSuccessful();
$I->seeCurrentRouteIs('admin.cart-rules.copy');
}
}

View File

@ -8,6 +8,7 @@ modules:
- Asserts
- Filesystem
- \Helper\Unit
- \Helper\DataMocker
- Webkul\Core\Helpers\Laravel5Helper:
environment_file: .env.testing
run_database_migrations: true

View File

@ -0,0 +1,55 @@
<?php
namespace Tests\Unit\Core\Commands;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use UnitTester;
use Webkul\BookingProduct\Models\BookingProduct;
use Webkul\BookingProduct\Models\BookingProductEventTicket;
use Webkul\Core\Helpers\Laravel5Helper;
use Webkul\Product\Models\Product;
class BookingCronCest
{
public function testBookingCronDeactivateSomeEvents(UnitTester $I): void
{
$index = $I->fake()->numberBetween(2, 6);
for ($i=0; $i<$index; $i++) {
$products[$i] = $I->haveProduct(Laravel5Helper::VIRTUAL_PRODUCT);
Product::query()->where('id', $products[$i]->id)->update(['type' => 'booking']);
if ($I->fake()->randomDigitNotNull <= 5) {
$availableTo = Carbon::now()->subMinutes($I->fake()->numberBetween(2, 59));
} else {
$availableTo = Carbon::now()->addMinutes($I->fake()->numberBetween(2, 59));
}
$bookingProducts[$i] = $I->have(BookingProduct::class, [
'type' => 'event',
'available_to' => $availableTo->toDateTimeString(),
'product_id' => $products[$i]->id,
]);
$I->have(BookingProductEventTicket::class,
['booking_product_id' => $bookingProducts[$i]->id]);
$products[$i]->refresh();
$I->assertNotFalse($products[$i]->status);
}
$I->callArtisan('booking:cron');
for ($i=0; $i<$index; $i++) {
$products[$i]->refresh();
if ($bookingProducts[$i]->available_to < Carbon::now()) {
$I->assertEquals(0, $products[$i]->status);
} else {
$I->assertEquals(1, $products[$i]->status);
}
}
}
}