Completed inventory indices

This commit is contained in:
jitendra 2022-10-10 19:20:40 +05:30
parent 37c688f6db
commit 09a34de6d3
25 changed files with 243 additions and 131 deletions

View File

@ -26,7 +26,7 @@ class Kernel extends ConsoleKernel
{
$schedule->command('booking:cron')->dailyAt('3:00');
$schedule->command('invoice:cron')->dailyAt('3:00');
$schedule->command('products:price-index')->dailyAt('24:00');
$schedule->command('product:index --type=price')->dailyAt('24:00');
}
/**

View File

@ -13,7 +13,6 @@ use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductCustomerGroupPriceRepository;
use Webkul\Inventory\Repositories\InventorySourceRepository;
use Webkul\Tax\Repositories\TaxCategoryRepository;
use Webkul\BookingProduct\Repositories\BookingProductRepository;
use Webkul\BookingProduct\Helpers\Booking as BookingHelper;
@ -53,7 +52,6 @@ class Booking extends Virtual
* @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param \Webkul\Product\Repositories\ProductVideoRepository $productVideoRepository
* @param \Webkul\Product\Repositories\ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository
* @param \Webkul\Inventory\Repositories\InventorySourceRepository $inventorySourceRepository
* @param \Webkul\Tax\Repositories\TaxCategoryRepository $taxCategoryRepository
* @param \Webkul\BookingProduct\Repositories\BookingProductRepository $bookingProductRepository
* @param \Webkul\BookingProduct\Helpers\BookingHelper $bookingHelper
@ -68,7 +66,6 @@ class Booking extends Virtual
ProductImageRepository $productImageRepository,
ProductVideoRepository $productVideoRepository,
ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository,
InventorySourceRepository $inventorySourceRepository,
TaxCategoryRepository $taxCategoryRepository,
protected BookingProductRepository $bookingProductRepository,
protected BookingHelper $bookingHelper
@ -83,7 +80,6 @@ class Booking extends Virtual
$productImageRepository,
$productVideoRepository,
$productCustomerGroupPriceRepository,
$inventorySourceRepository,
$taxCategoryRepository
);
}

View File

@ -15,24 +15,4 @@ class InventorySourceRepository extends Repository
{
return 'Webkul\Inventory\Contracts\InventorySource';
}
/**
* Returns channel inventory source ids.
*
* @return collection
*/
public function getChannelInventorySourceIds()
{
static $channelInventorySourceIds;
if ($channelInventorySourceIds) {
return $channelInventorySourceIds;
}
$found = core()->getCurrentChannel()->inventory_sources()
->where('status', 1)
->pluck('id');
return $channelInventorySourceIds = ($found ? $found : collect([]));
}
}

View File

@ -14,7 +14,7 @@ class Indexer extends Command
*
* @var string
*/
protected $signature = 'products:index';
protected $signature = 'products:index {--type=*}';
/**
* The console command description.
@ -41,12 +41,18 @@ class Indexer extends Command
*/
public function handle()
{
$indexers = ['price', 'inventory'];
if (! empty($this->option('type'))) {
$indexers = $this->option('type');
}
$batch = Bus::batch([])->dispatch();
while (true) {
$paginator = $this->productRepository->cursorPaginate(10);
$batch->add(new IndexerJob($paginator->items()));
$batch->add(new IndexerJob($paginator->items(), $indexers));
if (! $cursor = $paginator->nextCursor()) {
break;

View File

@ -24,6 +24,8 @@ return new class extends Migration
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
$table->timestamps();
});
}

View File

@ -27,9 +27,7 @@ class ConfigurableOption extends AbstractProduct
'parent',
'product.attribute_values',
'product.price_indices',
'product.inventory_sources',
'product.inventories',
'product.ordered_inventories',
'product.inventory_indices',
'images',
'videos',
])

View File

@ -2,27 +2,33 @@
namespace Webkul\Product\Helpers;
use Webkul\Core\Repositories\ChannelRepository;
use Webkul\Customer\Repositories\CustomerGroupRepository;
use Webkul\Product\Repositories\ProductPriceIndexRepository;
use Webkul\Product\Repositories\ProductInventoryIndexRepository;
use Webkul\Product\Helpers\Indexers\Flat\Product as FlatIndexer;
use Webkul\Product\Helpers\Indexers\Inventory\Product as InventoryIndexer;
class Indexer
{
/**
* Create a new command instance.
*
* @param \Webkul\Core\Repositories\ChannelRepository $channelRepository
* @param \Webkul\Customer\Repositories\CustomerGroupRepository $customerGroupRepository
* @param \Webkul\Product\Repositories\ProductPriceIndexRepository $productPriceIndexRepository
* @param \Webkul\Product\Repositories\ProductInventoryIndexRepository $productInventoryIndexRepository
* @param \Webkul\Product\Helpers\Indexers\Flat\Product $flatIndexer
* @param \Webkul\Product\Helpers\Indexers\Inventory\Product $inventoryIndexer
* @return void
*/
public function __construct(
protected ChannelRepository $channelRepository,
protected CustomerGroupRepository $customerGroupRepository,
protected ProductPriceIndexRepository $productPriceIndexRepository,
protected ProductInventoryIndexRepository $productInventoryIndexRepository,
protected FlatIndexer $flatIndexer
protected FlatIndexer $flatIndexer,
protected InventoryIndexer $inventoryIndexer
)
{
}
@ -31,13 +37,18 @@ class Indexer
* Refresh product indexes
*
* @param \Webkul\Product\Contracts\Product $product
* @param array $indexers
* @return void
*/
public function refresh($product)
public function refresh($product, array $indexers = ['price', 'inventory'])
{
$this->refreshPrice($product);
if (in_array('price', $indexers)) {
$this->refreshPrice($product);
}
$this->refreshInventory($product);
if (in_array('inventory', $indexers)) {
$this->refreshInventory($product);
}
}
/**
@ -81,5 +92,17 @@ class Indexer
*/
public function refreshInventory($product)
{
if (in_array($product, ['configurable', 'bundle', 'grouped', 'booking'])) {
return;
}
$channels = $this->channelRepository->all();
foreach ($channels as $channel) {
$this->productInventoryIndexRepository->updateOrCreate([
'channel_id' => $channel->id,
'product_id' => $product->id,
], $this->inventoryIndexer->setProduct($product)->getIndices($channel));
}
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace Webkul\Product\Helpers\Indexers\Inventory;
class Product
{
/**
* Product instance.
*
* @var \Webkul\Product\Contracts\Product
*/
protected $product;
/**
* Channel instance.
*
* @var \Webkul\Core\Contracts\Channel
*/
protected $channel;
/**
* Set current product
*
* @param \Webkul\Product\Contracts\Product $product
* @return \Webkul\Product\Helpers\Indexers\Inventory\Product
*/
public function setProduct($product)
{
$this->product = $product;
return $this;
}
/**
* Set channel
*
* @param \Webkul\Core\Contracts\Channel $channel
* @return \Webkul\Product\Helpers\Indexers\Inventory\Product
*/
public function setChannel($channel)
{
$this->channel = $channel;
return $this;
}
/**
* Returns product specific indices
*
* @param \Webkul\Core\Contracts\Channel $channel
* @return array
*/
public function getIndices($channel)
{
$this->setChannel($channel);
return [
'qty' => $this->getQuantity(),
];
}
/**
* Returns product remaining quantity
*
* @return integer
*/
public function getQuantity()
{
$channelInventorySourceIds = $this->channel->inventory_sources->where('status', 1)->pluck('id');
$qty = 0;
foreach ($this->product->inventories as $inventory) {
if (is_numeric($channelInventorySourceIds->search($inventory->inventory_source_id))) {
$qty += $inventory->qty;
}
}
$orderedInventory = $this->product->ordered_inventories
->where('channel_id', $this->channel->id)->first();
if ($orderedInventory) {
$qty -= $orderedInventory->qty;
}
return $qty;
}
}

View File

@ -42,7 +42,7 @@ abstract class AbstractIndexer
* Set current product
*
* @param \Webkul\Product\Contracts\Product $product
* @return \Webkul\Product\Helpers\ProductPriceIndex\AbstractPriceIndex
* @return \Webkul\Product\Helpers\Indexers\Price\AbstractPriceIndex
*/
public function setProduct($product)
{
@ -55,7 +55,7 @@ abstract class AbstractIndexer
* Set customer group
*
* @param \Webkul\Customer\Contracts\CustomerGroup $customerGroup
* @return \Webkul\Product\Helpers\ProductPriceIndex\AbstractPriceIndex
* @return \Webkul\Product\Helpers\Indexers\Price\AbstractPriceIndex
*/
public function setCustomerGroup($customerGroup)
{
@ -72,7 +72,7 @@ abstract class AbstractIndexer
*/
public function getIndices($customerGroup)
{
$this->customerGroup = $customerGroup;
$this->setCustomerGroup($customerGroup);
return [
'min_price' => ($minPrice = $this->getMinimalPrice()) ?? 0,

View File

@ -12,7 +12,7 @@ class Bundle extends AbstractIndexer
*/
public function getIndices($customerGroup)
{
$this->customerGroup = $customerGroup;
$this->setCustomerGroup($customerGroup);
return [
'min_price' => $this->getMinimalPrice() ?? 0,

View File

@ -12,7 +12,7 @@ class Configurable extends AbstractIndexer
*/
public function getIndices($customerGroup)
{
$this->customerGroup = $customerGroup;
$this->setCustomerGroup($customerGroup);
return [
'min_price' => $this->getMinimalPrice() ?? 0,

View File

@ -12,7 +12,7 @@ class Grouped extends AbstractIndexer
*/
public function getIndices($customerGroup)
{
$this->customerGroup = $customerGroup;
$this->setCustomerGroup($customerGroup);
return [
'min_price' => $this->getMinimalPrice() ?? 0,

View File

@ -21,15 +21,23 @@ class Indexer implements ShouldQueue
*/
protected $products;
/**
* @var array
*/
protected $indexers;
/**
* Create a new job instance.
*
* @param array $products
* @param array $indexers
* @return void
*/
public function __construct($products)
public function __construct($products, $indexers)
{
$this->products = $products;
$this->indexers = $indexers;
}
/**
@ -42,7 +50,7 @@ class Indexer implements ShouldQueue
$indexer = app(IndexerHelper::class);
foreach ($this->products as $product) {
$indexer->refresh($product);
$indexer->refresh($product, $this->indexers);
}
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Webkul\Product\Listeners;
use Webkul\Product\Helpers\Indexer;
class Order
{
/**
* Create a new listener instance.
*
* @param \Webkul\Product\Helpers\Indexer $indexer
* @return void
*/
public function __construct(protected Indexer $indexer)
{
}
/**
* After order is created
*
* @param \Webkul\Sale\Contracts\Order $order
* @return void
*/
public function afterCreate($order)
{
foreach ($order->all_items as $item) {
$this->indexer->refreshInventory($item->product);
}
}
/**
* After order is cancelled
*
* @param \Webkul\Sale\Contracts\Order $order
* @return void
*/
public function afterCancel($order)
{
foreach ($order->all_items as $item) {
$this->indexer->refreshInventory($item->product);
}
}
}

View File

@ -47,7 +47,7 @@ class Product
$this->refreshPriceIndices($product);
$this->refreshInventoryIndices($product);
$this->indexer->refreshInventory($product);
}
/**
@ -73,20 +73,10 @@ class Product
}
foreach ($products as $product) {
$this->indexer->refresh($product);
$this->indexer->refreshPrice($product);
}
}
/**
* Update or create product inventory indices
*
* @param \Webkul\Product\Contracts\Product $product
* @return void
*/
public function refreshInventoryIndices($product)
{
}
/**
* Returns parents bundle products associated with simple product
*

View File

@ -0,0 +1,31 @@
<?php
namespace Webkul\Product\Listeners;
use Webkul\Product\Helpers\Indexer;
class Refund
{
/**
* Create a new listener instance.
*
* @param \Webkul\Product\Helpers\Indexer $indexer
* @return void
*/
public function __construct(protected Indexer $indexer)
{
}
/**
* After refund is created
*
* @param \Webkul\Sale\Contracts\Refund $refund
* @return void
*/
public function afterCreate($refund)
{
foreach ($refund->order->all_items as $item) {
$this->indexer->refreshInventory($item->product);
}
}
}

View File

@ -12,20 +12,29 @@ class EventServiceProvider extends ServiceProvider
* @var array
*/
protected $listen = [
'catalog.attribute.create.after' => [
'catalog.attribute.create.after' => [
'Webkul\Product\Listeners\Attribute@afterCreate',
],
'catalog.attribute.update.after' => [
'catalog.attribute.update.after' => [
'Webkul\Product\Listeners\Attribute@afterUpdate',
],
'catalog.attribute.delete.before' => [
'Webkul\Product\Listeners\Attribute@beforeRemove',
],
'catalog.product.create.after' => [
'catalog.product.create.after' => [
'Webkul\Product\Listeners\Product@afterCreate',
],
'catalog.product.update.after' => [
'catalog.product.update.after' => [
'Webkul\Product\Listeners\Product@afterUpdate',
],
'checkout.order.save.after' => [
'Webkul\Product\Listeners\Order@afterCreate',
],
'sales.order.cancel.after' => [
'Webkul\Product\Listeners\Order@afterCancel',
],
'sales.refund.save.after' => [
'Webkul\Product\Listeners\Refund@afterCreate',
],
];
}
}

View File

@ -37,20 +37,4 @@ class ProductInventoryRepository extends Repository
]);
}
}
/**
* Check if product inventories are already loaded. If already loaded then load from it.
*
* @return object
*/
public function checkInLoadedProductInventories($product)
{
static $productInventories = [];
if (array_key_exists($product->id, $productInventories)) {
return $productInventories[$product->id];
}
return $productInventories[$product->id] = $product->inventories;
}
}

View File

@ -185,10 +185,8 @@ class ProductRepository extends Repository
'images',
'product.videos',
'product.attribute_values',
'product.inventory_sources',
'product.inventories',
'product.ordered_inventories',
'product.price_indices',
'product.inventory_indices',
'product.reviews',
])->scopeQuery(function ($query) use ($params, $categoryId) {
$prefix = DB::getTablePrefix();
@ -279,7 +277,7 @@ class ProductRepository extends Repository
$orderDirection = empty($params['order']) ? end($sortOptions) : $params['order'];
if (empty($params['sort'])) {
$this->checkSortAttributeAndGenerateQuery($qb, current($sortOptions), $orderDirection);
$this->checkSortAttributeAndGenerateQuery($qb, $sortOptions[0], $orderDirection);
} else {
$this->checkSortAttributeAndGenerateQuery($qb, $params['sort'], $orderDirection);
}

View File

@ -13,7 +13,6 @@ use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductCustomerGroupPriceRepository;
use Webkul\Inventory\Repositories\InventorySourceRepository;
use Webkul\Tax\Repositories\TaxCategoryRepository;
use Webkul\Product\DataTypes\CartItemValidationResult;
use Webkul\Product\Models\ProductFlat;
@ -126,7 +125,6 @@ abstract class AbstractType
* @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param \Webkul\Product\Repositories\ProductVideoRepository $productVideoRepository
* @param \Webkul\Product\Repositories\ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository
* @param \Webkul\Inventory\Repositories\InventorySourceRepository $inventorySourceRepository
* @param \Webkul\Tax\Repositories\TaxCategoryRepository $taxCategoryRepository
* @return void
*/
@ -139,7 +137,6 @@ abstract class AbstractType
protected ProductImageRepository $productImageRepository,
protected ProductVideoRepository $productVideoRepository,
protected ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository,
protected InventorySourceRepository $inventorySourceRepository,
protected TaxCategoryRepository $taxCategoryRepository
)
{
@ -614,26 +611,11 @@ abstract class AbstractType
*/
public function totalQuantity()
{
$total = 0;
$channelInventorySourceIds = $this->inventorySourceRepository->getChannelInventorySourceIds();
$productInventories = $this->productInventoryRepository->checkInLoadedProductInventories($this->product);
foreach ($productInventories as $inventory) {
if (is_numeric($channelInventorySourceIds->search($inventory->inventory_source_id))) {
$total += $inventory->qty;
}
if (! $inventoryIndex = $this->getInventoryIndex()) {
return 0;
}
$orderedInventory = $this->product->ordered_inventories
->where('channel_id', core()->getCurrentChannel()->id)->first();
if ($orderedInventory) {
$total -= $orderedInventory->qty;
}
return $total;
return $inventoryIndex->qty;
}
/**
@ -830,7 +812,7 @@ abstract class AbstractType
return false;
}
return $priceIndex->min_price != $this->product->regular_min_price;
return $priceIndex->min_price != $priceIndex->regular_min_price;
}
/**

View File

@ -10,7 +10,6 @@ use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductCustomerGroupPriceRepository;
use Webkul\Inventory\Repositories\InventorySourceRepository;
use Webkul\Tax\Repositories\TaxCategoryRepository;
use Webkul\Product\Repositories\ProductBundleOptionRepository;
use Webkul\Product\Repositories\ProductBundleOptionProductRepository;
@ -85,7 +84,6 @@ class Bundle extends AbstractType
* @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param \Webkul\Product\Repositories\ProductVideoRepository $productVideoRepository
* @param \Webkul\Product\Repositories\ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository
* @param \Webkul\Inventory\Repositories\InventorySourceRepository $inventorySourceRepository
* @param \Webkul\Tax\Repositories\TaxCategoryRepository $taxCategoryRepository
* @param \Webkul\Product\Repositories\ProductBundleOptionRepository $productBundleOptionRepository
* @param \Webkul\Product\Repositories\ProductBundleOptionProductRepository $productBundleOptionProductRepository
@ -101,7 +99,6 @@ class Bundle extends AbstractType
ProductImageRepository $productImageRepository,
ProductVideoRepository $productVideoRepository,
ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository,
InventorySourceRepository $inventorySourceRepository,
TaxCategoryRepository $taxCategoryRepository,
protected ProductBundleOptionRepository $productBundleOptionRepository,
protected ProductBundleOptionProductRepository $productBundleOptionProductRepository,
@ -117,7 +114,6 @@ class Bundle extends AbstractType
$productImageRepository,
$productVideoRepository,
$productCustomerGroupPriceRepository,
$inventorySourceRepository,
$taxCategoryRepository
);
}

View File

@ -779,25 +779,10 @@ class Configurable extends AbstractType
{
$total = 0;
$channelInventorySourceIds = core()->getCurrentChannel()
->inventory_sources
->where('status', 1)
->pluck('id');
foreach ($this->product->variants as $variant) {
foreach ($variant->inventories as $inventory) {
if (is_numeric($index = $channelInventorySourceIds->search($inventory->inventory_source_id))) {
$total += $inventory->qty;
}
}
$inventoryIndex = $variant->totalQuantity();
$orderedInventory = $variant->ordered_inventories
->where('channel_id', core()->getCurrentChannel()->id)
->first();
if ($orderedInventory) {
$total -= $orderedInventory->qty;
}
$total += $inventoryIndex->qty;
}
return $total;

View File

@ -10,7 +10,6 @@ use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductCustomerGroupPriceRepository;
use Webkul\Inventory\Repositories\InventorySourceRepository;
use Webkul\Tax\Repositories\TaxCategoryRepository;
use Webkul\Product\Repositories\ProductDownloadableLinkRepository;
use Webkul\Product\Repositories\ProductDownloadableSampleRepository;
@ -79,7 +78,6 @@ class Downloadable extends AbstractType
* @param \Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
* @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param \Webkul\Product\Repositories\ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository
* @param \Webkul\Inventory\Repositories\InventorySourceRepository $inventorySourceRepository
* @param \Webkul\Tax\Repositories\TaxCategoryRepository $taxCategoryRepository
* @param \Webkul\Product\Repositories\ProductDownloadableLinkRepository $productDownloadableLinkRepository
* @param \Webkul\Product\Repositories\ProductDownloadableSampleRepository $productDownloadableSampleRepository
@ -95,7 +93,6 @@ class Downloadable extends AbstractType
productImageRepository $productImageRepository,
ProductVideoRepository $productVideoRepository,
ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository,
InventorySourceRepository $inventorySourceRepository,
TaxCategoryRepository $taxCategoryRepository,
protected ProductDownloadableLinkRepository $productDownloadableLinkRepository,
protected ProductDownloadableSampleRepository $productDownloadableSampleRepository
@ -110,7 +107,6 @@ class Downloadable extends AbstractType
$productImageRepository,
$productVideoRepository,
$productCustomerGroupPriceRepository,
$inventorySourceRepository,
$taxCategoryRepository
);
}

View File

@ -10,7 +10,6 @@ use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductVideoRepository;
use Webkul\Product\Repositories\ProductCustomerGroupPriceRepository;
use Webkul\Inventory\Repositories\InventorySourceRepository;
use Webkul\Tax\Repositories\TaxCategoryRepository;
use Webkul\Product\Repositories\ProductGroupedProductRepository;
use Webkul\Product\Models\ProductFlat;
@ -67,7 +66,6 @@ class Grouped extends AbstractType
* @param \Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
* @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param \Webkul\Product\Repositories\ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository
* @param \Webkul\Inventory\Repositories\InventorySourceRepository $inventorySourceRepository
* @param \Webkul\Tax\Repositories\TaxCategoryRepository $taxCategoryRepository
* @param \Webkul\Product\Repositories\ProductGroupedProductRepository $productGroupedProductRepository
* @param \Webkul\Product\Repositories\ProductVideoRepository $productVideoRepository
@ -82,7 +80,6 @@ class Grouped extends AbstractType
ProductImageRepository $productImageRepository,
ProductVideoRepository $productVideoRepository,
ProductCustomerGroupPriceRepository $productCustomerGroupPriceRepository,
InventorySourceRepository $inventorySourceRepository,
TaxCategoryRepository $taxCategoryRepository,
protected ProductGroupedProductRepository $productGroupedProductRepository
)
@ -96,7 +93,6 @@ class Grouped extends AbstractType
$productImageRepository,
$productVideoRepository,
$productCustomerGroupPriceRepository,
$inventorySourceRepository,
$taxCategoryRepository
);
}

View File

@ -36,7 +36,7 @@ class Simple extends AbstractType
public function isSaleable()
{
return $this->checkInLoadedSaleableChecks($this->product, function ($product) {
if (! $product->status) {
if (! $this->product->status) {
return false;
}