diff --git a/packages/Webkul/Product/src/Database/Factories/ProductOrderedInventoryFactory.php b/packages/Webkul/Product/src/Database/Factories/ProductOrderedInventoryFactory.php new file mode 100644 index 000000000..b5139c73f --- /dev/null +++ b/packages/Webkul/Product/src/Database/Factories/ProductOrderedInventoryFactory.php @@ -0,0 +1,31 @@ + $this->faker->numberBetween(100, 200), + 'product_id' => Product::factory(), + 'channel_id' => 1, + ]; + } +} diff --git a/packages/Webkul/Product/src/Models/ProductOrderedInventory.php b/packages/Webkul/Product/src/Models/ProductOrderedInventory.php index ab5423cf4..992d93ac9 100644 --- a/packages/Webkul/Product/src/Models/ProductOrderedInventory.php +++ b/packages/Webkul/Product/src/Models/ProductOrderedInventory.php @@ -2,15 +2,29 @@ namespace Webkul\Product\Models; +use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Webkul\Inventory\Models\InventorySourceProxy; use Webkul\Core\Models\ChannelProxy; use Webkul\Product\Contracts\ProductOrderedInventory as ProductOrderedInventoryContract; +use Webkul\Product\Database\Factories\ProductOrderedInventoryFactory; class ProductOrderedInventory extends Model implements ProductOrderedInventoryContract { + use HasFactory; + + /** + * Timestamps. + * + * @var bool + */ public $timestamps = false; + /** + * Fillables. + * + * @var array + */ protected $fillable = [ 'qty', 'product_id', @@ -19,6 +33,8 @@ class ProductOrderedInventory extends Model implements ProductOrderedInventoryCo /** * Get the channel owns the inventory. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function channel() { @@ -27,9 +43,21 @@ class ProductOrderedInventory extends Model implements ProductOrderedInventoryCo /** * Get the product that owns the product inventory. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function product() { return $this->belongsTo(ProductProxy::modelClass()); } -} \ No newline at end of file + + /** + * Create a new factory instance for the model. + * + * @return Factory + */ + protected static function newFactory(): Factory + { + return ProductOrderedInventoryFactory::new (); + } +} diff --git a/packages/Webkul/Sales/src/Repositories/OrderItemRepository.php b/packages/Webkul/Sales/src/Repositories/OrderItemRepository.php index cd3803e8b..bc2574558 100755 --- a/packages/Webkul/Sales/src/Repositories/OrderItemRepository.php +++ b/packages/Webkul/Sales/src/Repositories/OrderItemRepository.php @@ -2,8 +2,6 @@ namespace Webkul\Sales\Repositories; -use Illuminate\Container\Container as App; -use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Log; use Webkul\Core\Eloquent\Repository; use Webkul\Sales\Contracts\OrderItem; @@ -11,16 +9,18 @@ use Webkul\Sales\Contracts\OrderItem; class OrderItemRepository extends Repository { /** - * Specify Model class name + * Specify model class name. * * @return string */ - function model() + public function model() { return OrderItem::class; } /** + * Create. + * * @param array $data * @return \Webkul\Sales\Contracts\OrderItem */ @@ -37,6 +37,8 @@ class OrderItemRepository extends Repository } /** + * Collect totals. + * * @param \Webkul\Sales\Contracts\OrderItem $orderItem * @return \Webkul\Sales\Contracts\OrderItem */ @@ -96,6 +98,8 @@ class OrderItemRepository extends Repository } /** + * Manage inventory. + * * @param \Webkul\Sales\Contracts\OrderItem $orderItem * @return void */ @@ -125,14 +129,14 @@ class OrderItemRepository extends Repository if (isset($item->qty_ordered)) { $qty = $item->qty_ordered; } else { - Log::info('OrderItem has no qty_ordered', ['orderItem' => $item, 'product' => $item->product]); + Log::info('OrderItem has no `qty_ordered`.', ['orderItem' => $item, 'product' => $item->product]); if (isset($item->parent->qty_ordered)) { $qty = $item->parent->qty_ordered; } else { - Log::info('OrderItem has no parent with qty_ordered', [ + Log::info('OrderItem has no parent with `qty_ordered`', [ 'orderItem' => $item, - 'parent' => $item->parent, - 'product' => $item->product + 'parent' => $item->parent, + 'product' => $item->product, ]); $qty = 1; } @@ -154,26 +158,14 @@ class OrderItemRepository extends Repository } /** - * Returns qty to product inventory after order cancelation + * Returns qty to product inventory after order cancellation. * * @param \Webkul\Sales\Contracts\OrderItem $orderItem * @return void */ public function returnQtyToProductInventory($orderItem) { - $orderedInventory = $orderItem->product->ordered_inventories() - ->where('channel_id', $orderItem->order->channel->id) - ->first(); - - if (! $orderedInventory) { - return; - } - - if (($qty = $orderedInventory->qty - ($orderItem->qty_ordered ? $orderItem->qty_to_cancel : $orderItem->parent->qty_ordered)) < 0) { - $qty = 0; - } - - $orderedInventory->update(['qty' => $qty]); + $this->updateProductOrderedInventories($orderItem); if ($orderItem->getTypeInstance()->isStockable()) { $shipmentItems = $orderItem->parent ? $orderItem->parent->shipment_items : $orderItem->shipment_items; @@ -182,20 +174,55 @@ class OrderItemRepository extends Repository foreach ($shipmentItems as $shipmentItem) { if ($orderItem->parent) { $shippedQty = $orderItem->qty_ordered - ? ($orderItem->qty_ordered / $orderItem->parent->qty_ordered) * $shipmentItem->qty - : $orderItem->parent->qty_ordered; + ? ($orderItem->qty_ordered / $orderItem->parent->qty_ordered) * $shipmentItem->qty + : $orderItem->parent->qty_ordered; } else { $shippedQty = $shipmentItem->qty; } - + $inventory = $orderItem->product->inventories() - // ->where('vendor_id', $data['vendor_id']) - ->where('inventory_source_id', $shipmentItem->shipment->inventory_source_id) - ->first(); - - $inventory->update(['qty' => $inventory->qty + $shippedQty]); + ->where('inventory_source_id', $shipmentItem->shipment->inventory_source_id) + ->first(); + + $inventory->update(['qty' => $inventory->qty + $shippedQty]); } } } } -} \ No newline at end of file + + /** + * Update product ordered quantity. + * + * @param \Webkul\Sales\Contracts\OrderItem $orderItem + * @return void + */ + public function updateProductOrderedInventories($orderItem) + { + $orderedInventory = $orderItem->product->ordered_inventories() + ->where('channel_id', $orderItem->order->channel->id) + ->first(); + + if (! $orderedInventory) { + return; + } + + $qty = ( + $orderedInventory->qty + + ( + isset($orderItem->qty_shipped) + ? $orderItem->qty_shipped + : $orderItem->parent->qty_shipped + ) + ) - ( + isset($orderItem->qty_ordered) + ? $orderItem->qty_ordered + : $orderItem->parent->qty_ordered + ); + + if ($qty < 0) { + $qty = 0; + } + + $orderedInventory->update(['qty' => $qty]); + } +} diff --git a/tests/unit/Sales/Order/OrderItemRepositoryCest.php b/tests/unit/Sales/Order/OrderItemRepositoryCest.php new file mode 100644 index 000000000..d257c865c --- /dev/null +++ b/tests/unit/Sales/Order/OrderItemRepositoryCest.php @@ -0,0 +1,80 @@ +getProperty('model'); + $property->setAccessible(true); + + $this->repository = $reflection->newInstanceWithoutConstructor(); + } + + public function testUpdateProductOrderedInventories(UnitTester $I) + { + /** + * Having 10 quantity in inventory. + */ + $product = $I->haveProduct(Bagisto::SIMPLE_PRODUCT, [ + 'productInventory' => ['qty' => 10], + ]); + + /** + * 2 quantities are on hold. + */ + $productOrderedInventory = $I->have(ProductOrderedInventory::class, [ + 'product_id' => $product->id, + 'qty' => 2, + ]); + + /** + * 5 quantities are shipped. + */ + $orderItem1 = $I->have(OrderItem::class, [ + 'product_id' => $product->id, + 'qty_ordered' => 5, + 'qty_shipped' => 5, + ]); + + /** + * 2 quantities are in queue. + */ + $orderItem2 = $I->have(OrderItem::class, [ + 'product_id' => $product->id, + 'qty_ordered' => 2, + 'qty_shipped' => 0, + ]); + + /** + * Now testing the repository method with shipped one cancelled. + */ + $this->repository->updateProductOrderedInventories($orderItem1); + $productOrderedInventory->refresh(); + $I->assertNotEquals(0, $productOrderedInventory->qty); + $I->assertEquals(2, $productOrderedInventory->qty); + + /** + * Now testing the repository method with pending one cancelled. + */ + $this->repository->updateProductOrderedInventories($orderItem2); + $productOrderedInventory->refresh(); + $I->assertEquals(0, $productOrderedInventory->qty); + } +}