Adding Supplier to Products - Complete Implementation Guide
Download Complete Files​
Get all the modified files including controllers, views, migrations, and JavaScript code to implement the supplier functionality.
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
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>×</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):
<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​
- Create Product: Test creating a product with supplier selection
- Edit Product: Test editing existing products and changing suppliers
- View Product: Verify supplier appears in product details modal
- Filter Products: Test filtering products by supplier in the index page
7.2 Bulk Operations​
- Select Multiple Products: Use checkboxes to select multiple products
- Bulk Update Supplier: Click bulk supplier button and assign supplier
- Verify Changes: Check that all selected products have the new supplier
7.3 Edge Cases​
- No Supplier Selected: Test with empty/null supplier values
- Invalid Supplier: Test with non-existent supplier IDs
- Permission Testing: Test with different user permissions
- 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