Skip to content

Commit

Permalink
Added the backorder field to products & variants
Browse files Browse the repository at this point in the history
  • Loading branch information
fulopattila122 committed Nov 23, 2023
1 parent e2f2c23 commit 17dff7d
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 7 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
- Added the payment dependent shipping fee calculator
- Added the `units_sold` and the `last_sale_at` attributes to the master product model (SUM/MAX from variants)
- Added the `Stockable` interface (Contracts)
- Added the `Stockable` interface to the `Product` and `MasterProductVariant` models
- Added the `backorder` field to products and product variants
- Fixed possible null return type on Billpayer::getName() when is_organization is true but the company name is null

## 3.x Series
Expand Down
2 changes: 2 additions & 0 deletions src/MasterProduct/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- Dropped Laravel 9 Support
- Dropped Enum v3 Support
- Changed minimal Enum requirement to v4.1
- Added the `Stockable` interface to the `MasterProductVariant` Model
- Added the `backorder` field to product variants

## 3.x Series

Expand Down
29 changes: 28 additions & 1 deletion src/MasterProduct/Models/MasterProductVariant.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Vanilo\Contracts\Dimension as DimensionContract;
use Vanilo\Contracts\Stockable;
use Vanilo\MasterProduct\Contracts\MasterProductVariant as MasterProductVariantContract;
use Vanilo\Support\Dto\Dimension;

Expand All @@ -29,6 +30,7 @@
* @property string $name
* @property string $sku
* @property float $stock
* @property float|null $backorder
* @property float|null $price
* @property float|null $original_price
* @property string|null $excerpt
Expand All @@ -44,7 +46,7 @@
*
* @method static MasterProductVariant create(array $attributes = [])
*/
class MasterProductVariant extends Model implements MasterProductVariantContract
class MasterProductVariant extends Model implements MasterProductVariantContract, Stockable
{
protected $guarded = ['id', 'created_at', 'updated_at'];

Expand All @@ -68,6 +70,31 @@ public function masterProduct(): BelongsTo
return $this->belongsTo(MasterProductProxy::modelClass(), 'master_product_id', 'id');
}

public function isOnStock(): bool
{
return $this->stock > 0;
}

public function onStockQuantity(): float
{
return (float) $this->stock;
}

public function isBackorderUnrestricted(): bool
{
return null === $this->backorder;
}

public function backorderQuantity(): ?float
{
return $this->backorder;
}

public function totalAvailableQuantity(): float
{
return $this->stock + (float) $this->backorder;
}

public function hasDimensions(): bool
{
return null !== $this->width && null !== $this->height && null !== $this->length;
Expand Down
154 changes: 154 additions & 0 deletions src/MasterProduct/Tests/Unit/MasterProductVariantStockTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?php

declare(strict_types=1);

/**
* Contains the MasterProductVariantStockTest class.
*
* @copyright Copyright (c) 2023 Vanilo UG
* @author Attila Fulop
* @license MIT
* @since 2023-11-23
*
*/

namespace Unit;

use Vanilo\MasterProduct\Models\MasterProduct;
use Vanilo\MasterProduct\Models\MasterProductVariant;
use Vanilo\MasterProduct\Tests\TestCase;

class MasterProductVariantStockTest extends TestCase
{
protected MasterProduct $master;

protected function setUp(): void
{
parent::setUp();

$this->master = MasterProduct::create(['name' => 'Yokka Magnitude Laptop']);
}

/** @test */
public function the_stock_can_be_set()
{
$product = MasterProductVariant::create([
'master_product_id' => $this->master->id,
'name' => 'Yokka Magnitude YM34 Laptop',
'sku' => '73781',
'stock' => 50,
]);

$this->assertEquals(50, $product->stock);
}

/** @test */
public function the_stock_field_value_returns_a_numeric_value()
{
$createdProduct = MasterProductVariant::create([
'master_product_id' => $this->master->id,
'name' => 'Yokka Magnitude YM34 Laptop',
'sku' => '73781',
'stock' => 73.5,
]);

$product = MasterProductVariant::find($createdProduct->id);

$this->assertTrue(\is_numeric($product->stock));
}

/** @test */
public function stock_field_value_defaults_to_zero()
{
$product = MasterProductVariant::create([
'master_product_id' => $this->master->id,
'name' => 'Yokka Magnitude YM34 Laptop',
'sku' => '73781',
]);

$this->assertEquals(0, $product->stock);
}

/** @test */
public function is_on_stock_returns_false_if_the_stock_is_equal_to_zero()
{
$product = MasterProductVariant::create([
'master_product_id' => $this->master->id,
'name' => 'Yokka Magnitude YM34 Laptop',
'sku' => '73781',
'stock' => 0,
]);

$this->assertFalse($product->isOnStock());
}

/** @test */
public function is_on_stock_returns_false_if_the_stock_is_less_than_zero()
{
$product = MasterProductVariant::create([
'master_product_id' => $this->master->id,
'name' => 'Yokka Magnitude YM34 Laptop',
'sku' => '73781',
'stock' => -8,
]);

$this->assertFalse($product->isOnStock());
}

/** @test */
public function backorder_value_can_be_specified()
{
$product = MasterProductVariant::create([
'master_product_id' => $this->master->id,
'name' => 'Yokka Mokka Screen 14',
'sku' => 'YMSCR1',
'backorder' => 16,
]);

$this->assertEquals(16, $product->backorder);
}

/** @test */
public function backorder_is_null_by_default()
{
$product = MasterProductVariant::create([
'master_product_id' => $this->master->id,
'name' => 'Yokka Mokka Screen 15',
'sku' => 'YMSCR2',
'backorder' => null,
]);

$this->assertNull($product->backorder);
}

/** @test */
public function it_implements_the_stockable_interface()
{
$product = MasterProductVariant::create([
'master_product_id' => $this->master->id,
'name' => 'Yokka Mokka Screen 16',
'sku' => 'YMSCR3',
'stock' => 3,
'backorder' => null,
]);

$this->assertTrue($product->isOnStock());
$this->assertEquals(3, $product->onStockQuantity());
$this->assertTrue($product->isBackorderUnrestricted());
$this->assertNull($product->backorderQuantity());
$this->assertEquals(3, $product->totalAvailableQuantity());

$backOrderProduct = MasterProductVariant::create([
'name' => 'Yokka Mokka Screen 17',
'sku' => 'YMSCR4',
'stock' => -1,
'backorder' => 4,
]);

$this->assertFalse($backOrderProduct->isOnStock());
$this->assertEquals(-1, $backOrderProduct->onStockQuantity());
$this->assertFalse($backOrderProduct->isBackorderUnrestricted());
$this->assertEquals(4, $backOrderProduct->backorderQuantity());
$this->assertEquals(3, $backOrderProduct->totalAvailableQuantity());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
public function up(): void
{
Schema::table('master_product_variants', function (Blueprint $table) {
$table->decimal('backorder', 15, 4, true)->nullable();
});
}

public function down(): void
{
Schema::table('master_product_variants', function (Blueprint $table) {
$table->dropColumn('backorder');
});
}
};
2 changes: 2 additions & 0 deletions src/Product/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- Dropped Laravel 9 Support
- Dropped Enum v3 Support
- Changed minimal Enum requirement to v4.1
- Added the `Stockable` interface to the Product Model
- Added the `backorder` field to products

## 3.x Series

Expand Down
4 changes: 0 additions & 4 deletions src/Product/Contracts/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,11 @@ interface Product
{
/**
* Returns whether the product is active (based on its state)
*
* @return bool
*/
public function isActive(): bool;

/**
* Returns the title of the product. If no `title` was given, returns the `name` of the product
*
* @return string
*/
public function title(): string;
}
26 changes: 25 additions & 1 deletion src/Product/Models/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Illuminate\Database\Eloquent\Model;
use Konekt\Enum\Eloquent\CastsEnums;
use Vanilo\Contracts\Dimension as DimensionContract;
use Vanilo\Contracts\Stockable;
use Vanilo\Product\Contracts\Product as ProductContract;
use Vanilo\Support\Dto\Dimension;

Expand All @@ -38,6 +39,8 @@
* @property float|null $width
* @property float|null $height
* @property float|null $length
* @property float $stock
* @property float|null $backorder
* @property string|null $ext_title
* @property string|null $meta_keywords
* @property string|null $meta_description
Expand All @@ -47,7 +50,7 @@
*
* @method static Product create(array $attributes)
*/
class Product extends Model implements ProductContract
class Product extends Model implements ProductContract, Stockable
{
use CastsEnums;
use Sluggable;
Expand All @@ -65,6 +68,7 @@ class Product extends Model implements ProductContract
'width' => 'float',
'length' => 'float',
'stock' => 'float',
'backorder' => 'float',
];

protected $enums = [
Expand Down Expand Up @@ -100,6 +104,26 @@ public function isOnStock(): bool
return $this->stock > 0;
}

public function onStockQuantity(): float
{
return (float) $this->stock;
}

public function isBackorderUnrestricted(): bool
{
return null === $this->backorder;
}

public function backorderQuantity(): ?float
{
return $this->backorder;
}

public function totalAvailableQuantity(): float
{
return $this->stock + (float) $this->backorder;
}

public function title(): string
{
return $this->ext_title ?? $this->name;
Expand Down
Loading

0 comments on commit 17dff7d

Please sign in to comment.