yonka's avatar

sell items as a set of items sometimes or each item as pcs

I build a database, after finished and start working, there is a problem arise from the seller, the seller is selling items, so the seller required to sell an items as either a set sometimes or as individual item. A set contains specific items with specific Quantities and price that are known for instance: a set has a name like AYZ and it has to have quantities like 1,2,..etc so if the buyer wants to buy AYZ 2sets, the buyer knows how many items each AYZ will have. So instead of mentioning all these items simply the buyer will say "I need AYZ 2sets" and that has even specific price (sum of all item prices it contains). the seller will tell each AYZ price like: $100x2sets = $200total. but the seller knows that $100 will divide to all other items that a set will contain. So I want to be able to make AYZ set and specify how many items it will have with specific quantities like : AYZ One set contains:

  1. A = 2pcs $10

  2. Y = 3pcs $15

  3. Z = 5pcs $7 so if the buyer wants AYZ 2set, it will multiply by 2 and will be like this: AYZ Two set contains:

  4. A = 2pcs*2 = 4pcs

  5. Y = 3pcs*2 = 6pcs

  6. Z = 5pcs*2 = 10pcs This is my database I am using right now, the seller is able to sell each item as an individual only, SO I want to able to add this functionality to my database. I have these tables

  7. SoldInventory Table,

class SoldInventory extends Model
{
    use HasFactory, CreatedUpdatedBy, softDeletes;

    protected $fillable = ['customer_id','store_id','date_of_sale','tracking_no','reference','reference_no','description','user_id'];

    public function soldInventoryItems()
    {
        return $this->hasMany(SoldInventoryItems::class);
    }

    public function customer()
    {
        return $this->belongsTo(Customer::class);
    }

    public function salesInvoice()
    {
        return $this->hasOne(SalesInvoice::class);
    }

    public function store()
    {
        return $this->belongsTo(Store::class);
    }
}
  1. SoldInventoryItems Table,
class SoldInventoryItems extends Model
{
    use HasFactory, CreatedUpdatedBy, softDeletes;

    protected $fillable = ['inventory_in_store_id','quantity','unit_price','sub_total'];


    public function soldInventory()
    {
        return $this->belongsTo(SoldInventory::class);
    }

    public function inventoryInStore() : BelongsTo
    {
        return $this->belongsTo(InventoryInStore::class);
    }
}```
3. InventoryInStore Table.

class InventoryInStore extends Model { use HasFactory, CreatedUpdatedBy, softDeletes;

protected $fillable = ['inventory_item_id','quantity','store_id'];

public function store()
{
    return $this->belongsTo(Store::class);
}

public function inventoryItem()
{
    return $this->belongsTo(InventoryItem::class);
}

public function soldInventoryItems()
{
    return $this->hasMany(SoldInventoryItems::class);
}

}

4. InventoryItem Table

class InventoryItem extends Model { use HasFactory, CreatedUpdatedBy, softDeletes;

public function item()
{
    return $this->belongsTo(Item::class);
}

public function category()
{
    return $this->belongsTo(Category::class);
}


public function inventoryInStores()
{
    return $this->hasMany(InventoryInStore::class);
}

}

I am using laravel and livewire.
0 likes
2 replies
LaryAI's avatar
Level 58

Absolutely, you can add selling "sets" (or bundles) functionality to your current Laravel database structure. Below is an approach to extend your design so you can sell both individual items and sets of items.


1. Database Design: Introducing Sets

Add a new table, item_sets, to represent a "Set" (like AYZ), and an item_set_components table to define which items (and quantities) are included.

Migration Example

php artisan make:model ItemSet -m
php artisan make:model ItemSetComponent -m

item_sets Table

// database/migrations/xxxx_xx_xx_create_item_sets_table.php
Schema::create('item_sets', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->decimal('price', 12, 2); // Total price for the entire set
    $table->timestamps();
});

item_set_components Table

// database/migrations/xxxx_xx_xx_create_item_set_components_table.php
Schema::create('item_set_components', function (Blueprint $table) {
    $table->id();
    $table->foreignId('item_set_id')->constrained()->onDelete('cascade');
    $table->foreignId('inventory_item_id')->constrained()->onDelete('cascade');
    $table->integer('quantity'); // Amount of this InventoryItem in the set
    $table->timestamps();
});

2. Eloquent Models

Define relationships:

ItemSet Model

class ItemSet extends Model
{
    protected $fillable = ['name', 'price'];

    public function components()
    {
        return $this->hasMany(ItemSetComponent::class);
    }
}

ItemSetComponent Model

class ItemSetComponent extends Model
{
    protected $fillable = ['item_set_id', 'inventory_item_id', 'quantity'];

    public function inventoryItem()
    {
        return $this->belongsTo(InventoryItem::class);
    }

    public function itemSet()
    {
        return $this->belongsTo(ItemSet::class);
    }
}

3. Selling a Set: Storing in SoldInventoryItems

Option 1: Store set sale in a different SoldInventoryItem row, using a nullable item_set_id.

Alter SoldInventoryItems:

  • Add item_set_id (nullable):
$table->foreignId('item_set_id')->nullable()->constrained()->onDelete('set null');

Then when selling a set (say, 2 x AYZ):

  • Create a SoldInventoryItems row representing 2 sets, with the item_set_id field set.
  • For inventory management and reporting, also decrease the quantities of all included InventoryItems by the correct multiple.

Or, when a set is sold, for updating stock and for clarity:

  • For each InventoryItem in the set, create a SoldInventoryItems row for the individual item, totaling the quantity (component quantity x set quantity), and indicate with a field (e.g., is_part_of_set, item_set_id).

4. Implementation in Your Sale Workflow

When selling a set (e.g. AYZ 2 sets):

Find the set definition:

$set = ItemSet::with('components.inventoryItem')->where('name', 'AYZ')->first();
$setQty = 2;
$setPrice = $set->price;

For each component:

foreach ($set->components as $component) {
    $quantityToSell = $component->quantity * $setQty;
    // Create SoldInventoryItems for each component
    SoldInventoryItems::create([
        'inventory_in_store_id' => $component->inventoryItem->inventoryInStores()->where('store_id', $storeId)->first()->id,
        'quantity' => $quantityToSell,
        'unit_price' => 0, // You may leave unit price as 0, or allocate proportionally if you wish
        'sub_total' => 0,
        'item_set_id' => $set->id, // So you know it was sold as part of a set
    ]);
    // Reduce stock in InventoryInStore accordingly
}

And create another SoldInventoryItems row for the set itself (optional for reporting):

SoldInventoryItems::create([
    // 'inventory_in_store_id' => null or optional,
    'quantity' => $setQty,
    'unit_price' => $set->price,
    'sub_total' => $set->price * $setQty,
    'item_set_id' => $set->id,
]);

You can adjust your Livewire component logic to allow the operator to choose between "individual" or "set" at sale time.


5. Display

When viewing a sale, you show:

  • If item_set_id is present, display it clearly as a set (with breakdown of its items).
  • Or group SoldInventoryItems by item_set_id to show sets.

Summary

  • Add item_sets and item_set_components for flexible set (bundle) support.
  • Update your sale flow to allow selecting/selling sets, and store all necessary rows for transparency and reporting.
  • Adjust inventory levels for individual items inside the sold sets.

This approach is scalable, reversible, and doesn't break your current single-item logic.

If you want code for a specific controller/store action or Livewire logic, just ask!

jlrdw's avatar

A set should be it's own inventory item.

Please or to participate in this conversation.