Merge pull request #12830 from snipe/features/adds_supplier_id_to_components

Added supplier to components and consumables
This commit is contained in:
snipe 2023-04-15 18:42:23 -07:00 committed by GitHub
commit 955539807c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 279 additions and 9 deletions

View file

@ -46,7 +46,7 @@ class ComponentsController extends Controller
$components = Company::scopeCompanyables(Component::select('components.*')
->with('company', 'location', 'category', 'assets'));
->with('company', 'location', 'category', 'assets', 'supplier'));
if ($request->filled('search')) {
$components = $components->TextSearch($request->input('search'));
@ -64,6 +64,10 @@ class ComponentsController extends Controller
$components->where('category_id', '=', $request->input('category_id'));
}
if ($request->filled('supplier_id')) {
$components->where('supplier_id', '=', $request->input('supplier_id'));
}
if ($request->filled('location_id')) {
$components->where('location_id', '=', $request->input('location_id'));
}
@ -90,6 +94,9 @@ class ComponentsController extends Controller
case 'company':
$components = $components->OrderCompany($order);
break;
case 'supplier':
$components = $components->OrderSupplier($order);
break;
default:
$components = $components->orderBy($column_sort, $order);
break;

View file

@ -75,6 +75,10 @@ class ConsumablesController extends Controller
$consumables->where('manufacturer_id', '=', $request->input('manufacturer_id'));
}
if ($request->filled('supplier_id')) {
$consumables->where('supplier_id', '=', $request->input('supplier_id'));
}
if ($request->filled('location_id')) {
$consumables->where('location_id','=',$request->input('location_id'));
}
@ -109,6 +113,9 @@ class ConsumablesController extends Controller
case 'company':
$consumables = $consumables->OrderCompany($order);
break;
case 'supplier':
$components = $consumables->OrderSupplier($order);
break;
default:
$consumables = $consumables->orderBy($column_sort, $order);
break;

View file

@ -23,11 +23,30 @@ class SuppliersController extends Controller
public function index(Request $request)
{
$this->authorize('view', Supplier::class);
$allowed_columns = ['id', 'name', 'address', 'phone', 'contact', 'fax', 'email', 'image', 'assets_count', 'licenses_count', 'accessories_count', 'url'];
$allowed_columns = ['
id',
'name',
'address',
'phone',
'contact',
'fax',
'email',
'image',
'assets_count',
'licenses_count',
'accessories_count',
'components_count',
'consumables_count',
'url',
];
$suppliers = Supplier::select(
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes']
)->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('accessories as accessories_count');
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes'])
->withCount('assets as assets_count')
->withCount('licenses as licenses_count')
->withCount('accessories as accessories_count')
->withCount('components as components_count')
->withCount('consumables as consumables_count');
if ($request->filled('search')) {

View file

@ -71,6 +71,7 @@ class ComponentsController extends Controller
$component = new Component();
$component->name = $request->input('name');
$component->category_id = $request->input('category_id');
$component->supplier_id = $request->input('supplier_id');
$component->location_id = $request->input('location_id');
$component->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$component->order_number = $request->input('order_number', null);
@ -145,6 +146,7 @@ class ComponentsController extends Controller
// Update the component data
$component->name = $request->input('name');
$component->category_id = $request->input('category_id');
$component->supplier_id = $request->input('supplier_id');
$component->location_id = $request->input('location_id');
$component->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$component->order_number = $request->input('order_number');

View file

@ -68,6 +68,7 @@ class ConsumablesController extends Controller
$consumable = new Consumable();
$consumable->name = $request->input('name');
$consumable->category_id = $request->input('category_id');
$consumable->supplier_id = $request->input('supplier_id');
$consumable->location_id = $request->input('location_id');
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$consumable->order_number = $request->input('order_number');
@ -144,6 +145,7 @@ class ConsumablesController extends Controller
$consumable->name = $request->input('name');
$consumable->category_id = $request->input('category_id');
$consumable->supplier_id = $request->input('supplier_id');
$consumable->location_id = $request->input('location_id');
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$consumable->order_number = $request->input('order_number');

View file

@ -37,6 +37,7 @@ class ComponentsTransformer
'id' => (int) $component->category->id,
'name' => e($component->category->name),
] : null,
'supplier' => ($component->supplier) ? ['id' => $component->supplier->id, 'name'=> e($component->supplier->name)] : null,
'order_number' => e($component->order_number),
'purchase_date' => Helper::getFormattedDateObject($component->purchase_date, 'date'),
'purchase_cost' => Helper::formatCurrencyOutput($component->purchase_cost),

View file

@ -31,6 +31,7 @@ class ConsumablesTransformer
'item_no' => e($consumable->item_no),
'location' => ($consumable->location) ? ['id' => (int) $consumable->location->id, 'name' => e($consumable->location->name)] : null,
'manufacturer' => ($consumable->manufacturer) ? ['id' => (int) $consumable->manufacturer->id, 'name' => e($consumable->manufacturer->name)] : null,
'supplier' => ($consumable->supplier) ? ['id' => $consumable->supplier->id, 'name'=> e($consumable->supplier->name)] : null,
'min_amt' => (int) $consumable->min_amt,
'model_number' => ($consumable->model_number != '') ? e($consumable->model_number) : null,
'remaining' => $consumable->numRemaining(),

View file

@ -41,6 +41,8 @@ class SuppliersTransformer
'assets_count' => (int) $supplier->assets_count,
'accessories_count' => (int) $supplier->accessories_count,
'licenses_count' => (int) $supplier->licenses_count,
'consumables_count' => (int) $supplier->consumables_count,
'components_count' => (int) $supplier->components_count,
'notes' => ($supplier->notes) ? e($supplier->notes) : null,
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),

View file

@ -33,7 +33,8 @@ class Component extends SnipeModel
'name' => 'required|min:3|max:255',
'qty' => 'required|integer|min:1',
'category_id' => 'required|integer|exists:categories,id',
'company_id' => 'integer|nullable',
'supplier_id' => 'nullable|integer|exists:suppliers,id',
'company_id' => 'integer|nullable|exists:companies,id',
'min_amt' => 'integer|min:0|nullable',
'purchase_date' => 'date_format:Y-m-d|nullable',
'purchase_cost' => 'numeric|nullable|gte:0',
@ -57,6 +58,7 @@ class Component extends SnipeModel
protected $fillable = [
'category_id',
'company_id',
'supplier_id',
'location_id',
'name',
'purchase_cost',
@ -86,6 +88,7 @@ class Component extends SnipeModel
'category' => ['name'],
'company' => ['name'],
'location' => ['name'],
'supplier' => ['name'],
];
@ -168,6 +171,18 @@ class Component extends SnipeModel
return $this->belongsTo(\App\Models\Category::class, 'category_id');
}
/**
* Establishes the item -> supplier relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function supplier()
{
return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id');
}
/**
* Establishes the component -> action logs relationship
*
@ -247,4 +262,17 @@ class Component extends SnipeModel
{
return $query->leftJoin('companies', 'components.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
}
/**
* Query builder scope to order on supplier
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderSupplier($query, $order)
{
return $query->leftJoin('suppliers', 'components.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
}
}

View file

@ -27,6 +27,7 @@ class Consumable extends SnipeModel
'requestable' => 'boolean',
'category_id' => 'integer',
'company_id' => 'integer',
'supplier_id',
'qty' => 'integer',
'min_amt' => 'integer',
];
@ -95,6 +96,7 @@ class Consumable extends SnipeModel
'company' => ['name'],
'location' => ['name'],
'manufacturer' => ['name'],
'supplier' => ['name'],
];
@ -249,6 +251,18 @@ class Consumable extends SnipeModel
return $this->belongsToMany(\App\Models\User::class, 'consumables_users', 'consumable_id', 'assigned_to')->withPivot('user_id')->withTrashed()->withTimestamps();
}
/**
* Establishes the item -> supplier relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function supplier()
{
return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id');
}
/**
* Determine whether to send a checkin/checkout email based on
@ -376,4 +390,17 @@ class Consumable extends SnipeModel
{
return $query->leftJoin('companies', 'consumables.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
}
/**
* Query builder scope to order on supplier
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderSupplier($query, $order)
{
return $query->leftJoin('suppliers', 'consumables.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
}
}

View file

@ -121,6 +121,30 @@ class Supplier extends SnipeModel
return $this->hasMany(\App\Models\Accessory::class, 'supplier_id');
}
/**
* Establishes the supplier -> component relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function components()
{
return $this->hasMany(\App\Models\Component::class, 'supplier_id');
}
/**
* Establishes the supplier -> component relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function consumables()
{
return $this->hasMany(\App\Models\Consumable::class, 'supplier_id');
}
/**
* Establishes the supplier -> asset maintenances relationship
*

View file

@ -59,6 +59,15 @@ class ComponentPresenter extends Presenter
'title' => trans('general.category'),
'formatter' => 'categoriesLinkObjFormatter',
], [
'field' => 'supplier',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.supplier'),
'visible' => false,
'formatter' => 'suppliersLinkObjFormatter',
],
[
'field' => 'qty',
'searchable' => false,
'sortable' => true,

View file

@ -53,6 +53,14 @@ class ConsumablePresenter extends Presenter
'sortable' => true,
'title' => trans('general.category'),
'formatter' => 'categoriesLinkObjFormatter',
], [
'field' => 'supplier',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.supplier'),
'visible' => false,
'formatter' => 'suppliersLinkObjFormatter',
], [
'field' => 'model_number',
'searchable' => true,

View file

@ -7,6 +7,7 @@ use App\Models\Company;
use App\Models\Component;
use App\Models\Location;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Supplier;
class ComponentFactory extends Factory
{
@ -35,6 +36,7 @@ class ComponentFactory extends Factory
'purchase_cost' => $this->faker->randomFloat(2),
'min_amt' => $this->faker->numberBetween($min = 1, $max = 2),
'company_id' => Company::factory(),
'supplier_id' => Supplier::factory(),
];
}

View file

@ -8,6 +8,7 @@ use App\Models\Consumable;
use App\Models\Manufacturer;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Supplier;
class ConsumableFactory extends Factory
{
@ -36,6 +37,7 @@ class ConsumableFactory extends Factory
'qty' => $this->faker->numberBetween(5, 10),
'min_amt' => $this->faker->numberBetween($min = 1, $max = 2),
'company_id' => Company::factory(),
'supplier_id' => Supplier::factory(),
];
}

View file

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddSupplierToComponents extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('components', function (Blueprint $table) {
if (!Schema::hasColumn('components', 'supplier_id')) {
$table->integer('supplier_id')->after('user_id')->nullable()->default(null);
}
});
Schema::table('consumables', function (Blueprint $table) {
if (!Schema::hasColumn('consumables', 'supplier_id')) {
$table->integer('supplier_id')->after('user_id')->nullable()->default(null);
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('components', function (Blueprint $table) {
if (Schema::hasColumn('components', 'supplier_id')) {
$table->dropColumn('supplier_id');
}
});
Schema::table('consumables', function (Blueprint $table) {
if (Schema::hasColumn('consumables', 'supplier_id')) {
$table->dropColumn('supplier_id');
}
});
}
}

View file

@ -17,6 +17,7 @@
@include ('partials.forms.edit.serial', ['fieldname' => 'serial'])
@include ('partials.forms.edit.company-select', ['translated_name' => trans('general.company'), 'fieldname' => 'company_id'])
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id'])
@include ('partials.forms.edit.supplier-select', ['translated_name' => trans('general.supplier'), 'fieldname' => 'supplier_id'])
@include ('partials.forms.edit.order_number')
@include ('partials.forms.edit.purchase_date')
@include ('partials.forms.edit.purchase_cost')

View file

@ -11,6 +11,7 @@
@include ('partials.forms.edit.company-select', ['translated_name' => trans('general.company'), 'fieldname' => 'company_id'])
@include ('partials.forms.edit.name', ['translated_name' => trans('admin/consumables/table.title')])
@include ('partials.forms.edit.category-select', ['translated_name' => trans('general.category'), 'fieldname' => 'category_id', 'required' => 'true', 'category_type' => 'consumable'])
@include ('partials.forms.edit.supplier-select', ['translated_name' => trans('general.supplier'), 'fieldname' => 'supplier_id'])
@include ('partials.forms.edit.manufacturer-select', ['translated_name' => trans('general.manufacturer'), 'fieldname' => 'manufacturer_id'])
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id'])
@include ('partials.forms.edit.model_number')

View file

@ -3,7 +3,7 @@
{{ Form::label($fieldname, $translated_name, array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7{{ ((isset($required)) && ($required=='true')) ? ' required' : '' }}">
<div class="col-md-7{{ (Helper::checkIfRequired($item, $fieldname)) ? ' required' : '' }}">
<select class="js-data-ajax" data-endpoint="categories/{{ (isset($category_type)) ? $category_type : 'assets' }}" data-placeholder="{{ trans('general.select_category') }}" name="{{ $fieldname }}" style="width: 100%" id="category_select_id" aria-label="{{ $fieldname }}" {!! ((isset($item)) && (Helper::checkIfRequired($item, $fieldname))) ? ' data-validation="required" required' : '' !!}>
@if ($category_id = old($fieldname, (isset($item)) ? $item->{$fieldname} : ''))
<option value="{{ $category_id }}" selected="selected" role="option" aria-selected="true" role="option">

View file

@ -2,14 +2,14 @@
{{ Form::label($fieldname, $translated_name, array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7{{ ((isset($required)) && ($required=='true')) ? ' required' : '' }}">
<div class="col-md-7{{ (Helper::checkIfRequired($item, $fieldname)) ? ' required' : '' }}">
<select class="js-data-ajax" data-endpoint="suppliers" data-placeholder="{{ trans('general.select_supplier') }}" name="{{ $fieldname }}" style="width: 100%" id="supplier_select" aria-label="{{ $fieldname }}">
@if ($supplier_id = old($fieldname, (isset($item)) ? $item->{$fieldname} : ''))
<option value="{{ $supplier_id }}" selected="selected" role="option" aria-selected="true" role="option">
<option value="{{ $supplier_id }}" selected="selected" role="option" aria-selected="true" role="option">
{{ (\App\Models\Supplier::find($supplier_id)) ? \App\Models\Supplier::find($supplier_id)->name : '' }}
</option>
@else
<option value="" role="option">{{ trans('general.select_supplier') }}</option>
<option value="" role="option">{{ trans('general.select_supplier') }}</option>
@endif
</select>
</div>

View file

@ -54,6 +54,8 @@
<th data-searchable="false" data-sortable="true" data-field="assets_count">{{ trans('admin/suppliers/table.assets') }}</th>
<th data-searchable="false" data-sortable="true" data-field="accessories_count">{{ trans('general.accessories') }}</th>
<th data-searchable="false" data-sortable="true" data-field="licenses_count">{{ trans('admin/suppliers/table.licenses') }}</th>
<th data-searchable="false" data-sortable="true" data-field="components_count">{{ trans('general.components') }}</th>
<th data-searchable="false" data-sortable="true" data-field="consumables_count">{{ trans('general.consumables') }}</th>
<th data-switchable="false" data-formatter="suppliersActionsFormatter" data-searchable="false" data-sortable="false" data-field="actions">{{ trans('table.actions') }}</th>
</tr>
</thead>

View file

@ -65,6 +65,30 @@
</a>
</li>
<li>
<a href="#components" data-toggle="tab">
<span class="hidden-lg hidden-md">
<i class="far fa-save fa-2x" aria-hidden="true"></i>
</span>
<span class="hidden-xs hidden-sm">
{{ trans('general.components') }}
{!! (($supplier->components) && ($supplier->components->count() > 0 )) ? '<badge class="badge badge-secondary">'.number_format($supplier->components->count()).'</badge>' : '' !!}
</span>
</a>
</li>
<li>
<a href="#consumables" data-toggle="tab">
<span class="hidden-lg hidden-md">
<i class="far fa-save fa-2x" aria-hidden="true"></i>
</span>
<span class="hidden-xs hidden-sm">
{{ trans('general.consumables') }}
{!! (($supplier->consumables) && ($supplier->consumables->count() > 0 )) ? '<badge class="badge badge-secondary">'.number_format($supplier->consumables->count()).'</badge>' : '' !!}
</span>
</a>
</li>
<li>
<a href="#maintenances" data-toggle="tab">
<span class="hidden-lg hidden-md">
@ -173,6 +197,59 @@
</div><!-- /.table-responsive -->
</div><!-- /.tab-pane -->
<div class="tab-pane" id="components">
<h2 class="box-title">{{ trans('general.components') }}</h2>
<div class="table table-responsive">
<table
data-columns="{{ \App\Presenters\ComponentPresenter::dataTableLayout() }}"
data-cookie-id-table="componentsListingTable"
data-pagination="true"
data-id-table="componentsListingTable"
data-search="true"
data-side-pagination="server"
data-show-columns="true"
data-show-fullscreen="true"
data-show-export="true"
data-show-refresh="true"
data-sort-order="asc"
id="accessoriesListingTable"
class="table table-striped snipe-table"
data-url="{{route('api.components.index', ['supplier_id' => $supplier->id]) }}"
data-export-options='{
"fileName": "export-suppliers-{{ str_slug($supplier->name) }}-components-{{ date('Y-m-d') }}",
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
}'>
</table>
</div><!-- /.table-responsive -->
</div><!-- /.tab-pane -->
<div class="tab-pane" id="consumables">
<h2 class="box-title">{{ trans('general.consumables') }}</h2>
<div class="table table-responsive">
<table
data-columns="{{ \App\Presenters\ConsumablePresenter::dataTableLayout() }}"
data-cookie-id-table="consumablesListingTable"
data-pagination="true"
data-id-table="consumablesListingTable"
data-search="true"
data-side-pagination="server"
data-show-columns="true"
data-show-fullscreen="true"
data-show-export="true"
data-show-refresh="true"
data-sort-order="asc"
id="accessoriesListingTable"
class="table table-striped snipe-table"
data-url="{{route('api.consumables.index', ['supplier_id' => $supplier->id]) }}"
data-export-options='{
"fileName": "export-suppliers-{{ str_slug($supplier->name) }}-consumables-{{ date('Y-m-d') }}",
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
}'>
</table>
</div><!-- /.table-responsive -->
</div><!-- /.tab-pane -->
<div class="tab-pane" id="maintenances">
<h2 class="box-title">{{ trans('admin/asset_maintenances/general.asset_maintenances') }}</h2>
<div class="table table-responsive">