Skip to main content

Adding Supplier to Products - Complete Implementation Guide

Download Complete Files​

Download All Updated Files

Get all the modified files including controllers, views, migrations, and JavaScript code to implement the supplier functionality.

Download files

Overview​

This guide will walk you through the complete process of adding supplier functionality to your Ultimate POS products. This feature allows you to:

  • Assign suppliers to products during creation and editing
  • Filter products by supplier in the products list
  • Bulk update suppliers for multiple products
  • View supplier information in product details

Adding Supplier to Products - Create Adding Supplier to Products - Index Adding Supplier to Products - Bulk

Step 1: Database Structure​

Migration and Direct SQL​

Ultimate POS already has a contacts table that stores suppliers. We just need to add a foreign key to the products table.

Create Migration:

php artisan make:migration add_supplier_id_to_products_table

Migration Content:

<?php

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

return new class extends Migration
{
public function up()
{
Schema::table('products', function (Blueprint $table) {
$table->unsignedInteger('supplier_id')->nullable()->after('brand_id');
$table->foreign('supplier_id')->references('id')->on('contacts')->onDelete('set null');
$table->index('supplier_id');
});
}

public function down()
{
Schema::table('products', function (Blueprint $table) {
$table->dropForeign(['supplier_id']);
$table->dropIndex(['supplier_id']);
$table->dropColumn('supplier_id');
});
}
};

Direct SQL (Alternative):

-- Add supplier_id column to products table
ALTER TABLE `products`
ADD COLUMN `supplier_id` BIGINT UNSIGNED NULL AFTER `brand_id`;

-- Add foreign key constraint
ALTER TABLE `products`
ADD CONSTRAINT `fk_products_supplier_id`
FOREIGN KEY (`supplier_id`) REFERENCES `contacts`(`id`) ON DELETE SET NULL;

-- Add index for better performance
ALTER TABLE `products`
ADD INDEX `idx_products_supplier_id` (`supplier_id`);

Run Migration:

php artisan migrate

Step 2: Model Updates​

Update Product Model​

File: app/Product.php

Add relationship method:

/**
* Get the supplier that owns the product.
*/
public function supplier()
{
return $this->belongsTo(\App\Contact::class, 'supplier_id')
->whereIn('type', ['supplier', 'both'])
->active();
}

File: app/Contact.php

Add relationship method:

    /**
* Get products supplied by this supplier
*/
public function products()
{
return $this->hasMany(\App\Product::class, 'supplier_id');
}

/**
* Get products supplied by this supplier with stock enabled
*/
public function stockProducts()
{
return $this->hasMany(\App\Product::class, 'supplier_id')->where('enable_stock', 1);
}

Step 3: Controller Updates​

ProductController Changes​

File: app/Http/Controllers/ProductController.php

3.1 Update index() method​

Add supplier join to query (around line 50):

->leftJoin('contacts as suppliers', 'products.supplier_id', '=', 'suppliers.id')

Add supplier to select statement (around line 80):

'suppliers.name as supplier'

Add supplier filter to query (around line 120):

$supplier_id = request()->get('supplier_id', null);
if (!empty($supplier_id)) {
$products->where('products.supplier_id', $supplier_id);
}

Add supplier column to DataTables (around line 100):

->addColumn('supplier', function ($row) {
return $row->supplier ?? '--';
})

Update rawColumns:

->rawColumns(['action', 'image', 'mass_delete', 'product', 'selling_price', 'purchase_price', 'category', 'current_stock', 'supplier'])

3.2 Update create() method​

Add suppliers dropdown (around line 150):

$suppliers = \App\Contact::suppliersDropdown($business_id);

Update compact statement:

return view('product.create')
->with(compact('categories', 'brands', 'units', 'taxes', 'barcode_types',
'default_profit_percent', 'tax_attributes', 'barcode_default', 'business_locations',
'duplicate_product', 'sub_categories', 'rack_details', 'selling_price_group_count',
'module_form_parts', 'product_types', 'common_settings', 'warranties',
'pos_module_data', 'suppliers'));

3.3 Update store() method​

Add 'supplier_id' to form_fields array (around line 170):

$form_fields = ['name', 'brand_id', 'unit_id', 'category_id', 'supplier_id', 'tax', 'type', 'barcode_type', 'sku', 'alert_quantity', 'tax_type', 'weight', 'product_description', 'sub_unit_ids', 'preparation_time_in_minutes', 'product_custom_field1', /* ... rest of custom fields ... */];

3.4 Update edit() method​

Add suppliers dropdown (around line 350):

$suppliers = \App\Contact::suppliersDropdown($business_id);

Update compact statement:

return view('product.edit')
->with(compact('categories', 'brands', 'units', 'sub_units', 'taxes', 'tax_attributes',
'barcode_types', 'product', 'sub_categories', 'default_profit_percent', 'business_locations',
'rack_details', 'selling_price_group_count', 'module_form_parts', 'product_types',
'common_settings', 'warranties', 'pos_module_data', 'alert_quantity', 'suppliers'));

3.5 Update update() method​

Add 'supplier_id' to product_details array (around line 400):

$product_details = $request->only(['name', 'brand_id', 'unit_id', 'category_id', 'supplier_id', 'tax', 'barcode_type', 'sku', 'alert_quantity', 'tax_type', 'weight', 'product_description', 'sub_unit_ids', 'preparation_time_in_minutes', /* ... rest of custom fields ... */]);

Add manual assignment:

$product->supplier_id = $product_details['supplier_id'];

3.6 Update view() method​

Add supplier to with array (around line 800):

$product = Product::where('business_id', $business_id)
->with(['brand', 'unit', 'category', 'sub_category', 'product_tax', 'variations', 'variations.product_variation', 'variations.group_prices', 'variations.media', 'product_locations', 'warranty', 'media', 'supplier'])
->findOrFail($id);

3.7 Add bulk supplier update method​

public function bulkUpdateSupplier(Request $request)
{
if (!auth()->user()->can('product.update')) {
abort(403, 'Unauthorized action.');
}

try {
$selected_products = $request->input('selected_products');
$supplier_id = $request->input('supplier_id');
$business_id = $request->session()->get('user.business_id');

if (empty($selected_products)) {
$output = [
'success' => 0,
'msg' => __('lang_v1.no_products_selected')
];
return $output;
}

$product_ids = explode(',', $selected_products);

DB::beginTransaction();

Product::where('business_id', $business_id)
->whereIn('id', $product_ids)
->update(['supplier_id' => $supplier_id]);

DB::commit();

$output = [
'success' => 1,
'msg' => __('lang_v1.supplier_updated_success')
];
} catch (\Exception $e) {
DB::rollBack();
\Log::emergency("File:" . $e->getFile() . "Line:" . $e->getLine() . "Message:" . $e->getMessage());

$output = [
'success' => 0,
'msg' => __('messages.something_went_wrong')
];
}

return $output;
}

3.8 Update other methods​

quickAdd() method:

$suppliers = \App\Contact::suppliersDropdown($business_id);

saveQuickProduct() method:

// Add 'supplier_id' to form_fields array

bulkEdit() and bulkUpdate() methods:

// Add supplier handling as shown in previous implementation guide

Step 4: Routes​

File: routes/web.php

Add the bulk supplier update route:

Route::post('/products/bulk-update-supplier', [ProductController::class, 'bulkUpdateSupplier'])
->name('products.bulk-update-supplier');

Step 5: View Updates​

5.1 Product Index Page​

File: resources/views/product/index.blade.php

Add supplier filter (after brand filter around line 90):

<div class="col-md-3">
<div class="form-group">
{!! Form::label('supplier_id', __('contact.supplier') . ':') !!}
{!! Form::select('supplier_id', $suppliers ?? [], null, [
'class' => 'form-control select2',
'style' => 'width:100%',
'id' => 'product_list_filter_supplier_id',
'placeholder' => __('lang_v1.all'),
]) !!}
</div>
</div>

Update JavaScript DataTable configuration:

Add to ajax data function:

d.supplier_id = $('#product_list_filter_supplier_id').val();

Add to columns array (after brand column):

{
data: 'supplier',
name: 'suppliers.name'
},

Update change event listener:

$(document).on('change',
'#product_list_filter_type, #product_list_filter_category_id, #product_list_filter_brand_id, #product_list_filter_supplier_id, #product_list_filter_unit_id, #product_list_filter_tax_id, #location_id, #active_state, #repair_model_id',
function() {
// existing code
});

Add bulk supplier modal:

<!-- Bulk Supplier Update Modal -->
<div class="modal fade" id="bulk_supplier_modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
<h4 class="modal-title">@lang('lang_v1.bulk_update_supplier')</h4>
</div>
<form id="bulk_supplier_form" method="POST" action="{{ route('products.bulk-update-supplier') }}">
@csrf
<div class="modal-body">
<div class="form-group">
<label for="bulk_supplier_id">@lang('contact.supplier'):</label>
<select name="supplier_id" id="bulk_supplier_id" class="form-control select2" style="width: 100%;">
<option value="">@lang('lang_v1.none')</option>
@if(isset($suppliers))
@foreach($suppliers as $id => $name)
@if($id != '')
<option value="{{ $id }}">{{ $name }}</option>
@endif
@endforeach
@endif
</select>
</div>
<input type="hidden" name="selected_products" id="bulk_selected_products">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">@lang('messages.close')</button>
<button type="submit" class="btn btn-primary">@lang('messages.update')</button>
</div>
</form>
</div>
</div>
</div>

Add bulk supplier JavaScript:

// Bulk supplier update functionality
$(document).on('click', '.bulk-update-supplier', function(e) {
e.preventDefault();
var selected_rows = getSelectedRows();

if (selected_rows.length > 0) {
$('#bulk_selected_products').val(selected_rows);
$('#bulk_supplier_modal').modal('show');

$('#bulk_supplier_id').select2({
dropdownParent: $('#bulk_supplier_modal')
});
} else {
swal('@lang("lang_v1.no_row_selected")');
}
});

$(document).on('submit', '#bulk_supplier_form', function(e) {
e.preventDefault();
var form = $(this);
var data = form.serialize();

$.ajax({
method: 'POST',
url: form.attr('action'),
dataType: 'json',
data: data,
beforeSend: function(xhr) {
form.find('button[type="submit"]').attr('disabled', true);
},
success: function(result) {
if (result.success == 1) {
$('#bulk_supplier_modal').modal('hide');
toastr.success(result.msg);
product_table.ajax.reload();
} else {
toastr.error(result.msg);
}
form.find('button[type="submit"]').attr('disabled', false);
},
error: function(xhr) {
toastr.error('@lang("messages.something_went_wrong")');
form.find('button[type="submit"]').attr('disabled', false);
}
});
});

5.2 Product Table​

File: resources/views/product/partials/product_list.blade.php

Add supplier column header (after brand):

<th>@lang('contact.supplier')</th>

Add bulk supplier button (in tfoot after existing buttons):

&nbsp;
<button type="button" class="tw-dw-btn tw-dw-btn-xs tw-dw-btn-outline tw-dw-btn-info bulk-update-supplier">
<i class="fa fa-users"></i> @lang('lang_v1.bulk_update_supplier')
</button>

5.3 Product Forms​

Create Form (resources/views/product/create.blade.php):

Add after brand field:

<div class="col-sm-4">
<div class="form-group">
{!! Form::label('supplier_id', __('contact.supplier') . ':') !!}
<div class="input-group">
{!! Form::select('supplier_id', $suppliers, !empty($duplicate_product->supplier_id) ? $duplicate_product->supplier_id : null, ['placeholder' => __('messages.please_select'), 'class' => 'form-control select2']); !!}
<span class="input-group-btn">
<button type="button" @if(!auth()->user()->can('supplier.create')) disabled @endif class="btn btn-default bg-white btn-flat btn-modal" data-href="{{action([\App\Http\Controllers\ContactController::class, 'create'], ['quick_add' => true, 'type' => 'supplier'])}}" title="@lang('contact.add_supplier')" data-container=".view_modal"><i class="fa fa-plus-circle text-primary fa-lg"></i></button>
</span>
</div>
</div>
</div>

Edit Form (resources/views/product/edit.blade.php):

Add after brand field:

<div class="col-sm-4">
<div class="form-group">
{!! Form::label('supplier_id', __('contact.supplier') . ':') !!}
<div class="input-group">
{!! Form::select('supplier_id', $suppliers, $product->supplier_id, ['placeholder' => __('messages.please_select'), 'class' => 'form-control select2']); !!}
<span class="input-group-btn">
<button type="button" @if(!auth()->user()->can('supplier.create')) disabled @endif class="btn btn-default bg-white btn-flat btn-modal" data-href="{{action([\App\Http\Controllers\ContactController::class, 'create'], ['quick_add' => true, 'type' => 'supplier'])}}" title="@lang('contact.add_supplier')" data-container=".view_modal"><i class="fa fa-plus-circle text-primary fa-lg"></i></button>
</span>
</div>
</div>
</div>

5.4 Product View Modal​

File: resources/views/product/view-modal.blade.php

Add supplier information after brand:

<b>@lang('contact.supplier'): </b>
{{$product->supplier->name ?? '--' }}<br>

5.5 Quick Add Form​

File: resources/views/product/partials/quick_add_product.blade.php

Add after brand field:

<div class="col-sm-6">
<div class="form-group">
{!! Form::label('supplier_id', __('contact.supplier') . ':') !!}
<div class="input-group">
{!! Form::select('supplier_id', $suppliers, null, ['placeholder' => __('messages.please_select'), 'class' => 'form-control select2']); !!}
<span class="input-group-btn">
<button type="button" @if(!auth()->user()->can('supplier.create')) disabled @endif class="btn btn-default bg-white btn-flat btn-modal" data-href="{{action([\App\Http\Controllers\ContactController::class, 'create'], ['quick_add' => true, 'type' => 'supplier'])}}" title="@lang('contact.add_supplier')" data-container=".view_modal"><i class="fa fa-plus-circle text-primary fa-lg"></i></button>
</span>
</div>
</div>
</div>

Step 6: Language Files​

File: resources/lang/en/lang_v1.php

Add these translations:

'bulk_update_supplier' => 'Bulk Update Supplier',
'supplier_updated_success' => 'Supplier updated successfully',
'no_products_selected' => 'No products selected',

Step 7: Testing​

7.1 Basic Functionality​

  1. Create Product: Test creating a product with supplier selection
  2. Edit Product: Test editing existing products and changing suppliers
  3. View Product: Verify supplier appears in product details modal
  4. Filter Products: Test filtering products by supplier in the index page

7.2 Bulk Operations​

  1. Select Multiple Products: Use checkboxes to select multiple products
  2. Bulk Update Supplier: Click bulk supplier button and assign supplier
  3. Verify Changes: Check that all selected products have the new supplier

7.3 Edge Cases​

  1. No Supplier Selected: Test with empty/null supplier values
  2. Invalid Supplier: Test with non-existent supplier IDs
  3. Permission Testing: Test with different user permissions
  4. Large Datasets: Test with many products selected for bulk update

Step 8: Optional Enhancements​

8.1 Supplier Statistics​

Add supplier-based reports showing:

  • Products per supplier
  • Stock levels by supplier
  • Purchase history by supplier

8.2 Advanced Filtering​

Add more complex filtering options:

  • Products without suppliers
  • Supplier-based stock alerts
  • Multi-supplier selection

8.3 Import/Export​

Update product import/export to include supplier information:

  • CSV import with supplier names
  • Excel export with supplier details

Conclusion​

You have successfully implemented supplier functionality for products in Ultimate POS. This feature provides:

  • ✅ Supplier assignment during product creation/editing
  • ✅ Supplier filtering in product listings
  • ✅ Bulk supplier updates for multiple products
  • ✅ Supplier information in product details
  • ✅ Integration with existing supplier management

The implementation follows Ultimate POS conventions and maintains compatibility with existing features.

💛 Support this project

Binance ID:

478036326
Premium Login