Added: Adjustment Module

This commit is contained in:
Fahim 2021-07-27 21:32:22 +06:00
parent 7b7d0c9389
commit b800ef883a
56 changed files with 1360 additions and 30 deletions

View File

View File

@ -0,0 +1,5 @@
<?php
return [
'name' => 'Adjustment'
];

View File

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAdjustmentsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('adjustments', function (Blueprint $table) {
$table->id();
$table->date('date');
$table->string('reference');
$table->text('note')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('adjustments');
}
}

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAdjustedProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('adjusted_products', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('adjustment_id');
$table->unsignedBigInteger('product_id');
$table->integer('quantity');
$table->string('type');
$table->foreign('adjustment_id')->references('id')->on('adjustments')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('adjusted_products');
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Modules\Adjustment\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class AdjustmentDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
// $this->call("OthersTableSeeder");
}
}

View File

View File

@ -0,0 +1,24 @@
<?php
namespace Modules\Adjustment\Entities;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Modules\Product\Entities\Product;
class AdjustedProduct extends Model
{
use HasFactory;
protected $guarded = [];
protected $with = ['product'];
public function adjustment() {
return $this->belongsTo(Adjustment::class, 'adjustment_id', 'id');
}
public function product() {
return $this->belongsTo(Product::class, 'product_id', 'id');
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Modules\Adjustment\Entities;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Carbon;
class Adjustment extends Model
{
use HasFactory;
protected $guarded = [];
public function getDateAttribute($value) {
return Carbon::parse($value)->format('M d, Y');
}
public function adjustedProducts() {
return $this->hasMany(AdjustedProduct::class, 'adjustment_id', 'id');
}
}

View File

@ -0,0 +1,166 @@
<?php
namespace Modules\Adjustment\Http\Controllers;
use App\DataTables\AdjustmentsDataTable;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Modules\Adjustment\Entities\AdjustedProduct;
use Modules\Adjustment\Entities\Adjustment;
use Modules\Product\Entities\Product;
class AdjustmentController extends Controller
{
public function index(AdjustmentsDataTable $dataTable) {
abort_if(Gate::denies('access_adjustments'), 403);
return $dataTable->render('adjustment::index');
}
public function create() {
abort_if(Gate::denies('create_adjustments'), 403);
return view('adjustment::create');
}
public function store(Request $request) {
abort_if(Gate::denies('create_adjustments'), 403);
$request->validate([
'reference' => 'required|string|max:255',
'date' => 'required|date',
'note' => 'nullable|string|max:1000',
'product_ids' => 'required',
'quantities' => 'required',
'types' => 'required'
]);
DB::transaction(function () use ($request) {
$adjustment = Adjustment::create([
'reference' => $request->reference,
'date' => $request->date,
'note' => $request->note
]);
foreach ($request->product_ids as $key => $id) {
AdjustedProduct::create([
'adjustment_id' => $adjustment->id,
'product_id' => $id,
'quantity' => $request->quantities[$key],
'type' => $request->types[$key]
]);
$product = Product::findOrFail($id);
if ($request->types[$key] == 'add') {
$product->update([
'product_quantity' => $product->product_quantity + $request->quantities[$key]
]);
} elseif ($request->types[$key] == 'sub') {
$product->update([
'product_quantity' => $product->product_quantity - $request->quantities[$key]
]);
}
}
});
toast('Adjustment Created!', 'success');
return redirect()->route('adjustments.index');
}
public function show(Adjustment $adjustment) {
abort_if(Gate::denies('show_adjustments'), 403);
return view('adjustment::show', compact('adjustment'));
}
public function edit(Adjustment $adjustment) {
abort_if(Gate::denies('edit_adjustments'), 403);
return view('adjustment::edit', compact('adjustment'));
}
public function update(Request $request, Adjustment $adjustment) {
abort_if(Gate::denies('edit_adjustments'), 403);
$request->validate([
'reference' => 'required|string|max:255',
'date' => 'required|date',
'note' => 'nullable|string|max:1000',
'product_ids' => 'required',
'quantities' => 'required',
'types' => 'required'
]);
DB::transaction(function () use ($request, $adjustment) {
$adjustment->update([
'reference' => $request->reference,
'date' => $request->date,
'note' => $request->note
]);
foreach ($adjustment->adjustedProducts as $adjustedProduct) {
$product = Product::findOrFail($adjustedProduct->product->id);
if ($adjustedProduct->type == 'add') {
$product->update([
'product_quantity' => $product->product_quantity - $adjustedProduct->quantity
]);
} elseif ($adjustedProduct->type == 'sub') {
$product->update([
'product_quantity' => $product->product_quantity + $adjustedProduct->quantity
]);
}
$adjustedProduct->delete();
}
foreach ($request->product_ids as $key => $id) {
AdjustedProduct::create([
'adjustment_id' => $adjustment->id,
'product_id' => $id,
'quantity' => $request->quantities[$key],
'type' => $request->types[$key]
]);
$product = Product::findOrFail($id);
if ($request->types[$key] == 'add')
{
$product->update([
'product_quantity' => $product->product_quantity + $request->quantities[$key]
]);
} elseif ($request->types[$key] == 'sub') {
$product->update([
'product_quantity' => $product->product_quantity - $request->quantities[$key]
]);
}
}
});
toast('Adjustment Updated!', 'info');
return redirect()->route('adjustments.index');
}
public function destroy(Adjustment $adjustment) {
abort_if(Gate::denies('delete_adjustments'), 403);
$adjustment->delete();
toast('Adjustment Deleted!', 'warning');
return redirect()->route('adjustments.index');
}
}

View File

View File

@ -0,0 +1,112 @@
<?php
namespace Modules\Adjustment\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Factory;
class AdjustmentServiceProvider extends ServiceProvider
{
/**
* @var string $moduleName
*/
protected $moduleName = 'Adjustment';
/**
* @var string $moduleNameLower
*/
protected $moduleNameLower = 'adjustment';
/**
* Boot the application events.
*
* @return void
*/
public function boot()
{
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations'));
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->register(RouteServiceProvider::class);
}
/**
* Register config.
*
* @return void
*/
protected function registerConfig()
{
$this->publishes([
module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'),
], 'config');
$this->mergeConfigFrom(
module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower
);
}
/**
* Register views.
*
* @return void
*/
public function registerViews()
{
$viewPath = resource_path('views/modules/' . $this->moduleNameLower);
$sourcePath = module_path($this->moduleName, 'Resources/views');
$this->publishes([
$sourcePath => $viewPath
], ['views', $this->moduleNameLower . '-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
}
/**
* Register translations.
*
* @return void
*/
public function registerTranslations()
{
$langPath = resource_path('lang/modules/' . $this->moduleNameLower);
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
} else {
$this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower);
}
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [];
}
private function getPublishableViewPaths(): array
{
$paths = [];
foreach (\Config::get('view.paths') as $path) {
if (is_dir($path . '/modules/' . $this->moduleNameLower)) {
$paths[] = $path . '/modules/' . $this->moduleNameLower;
}
}
return $paths;
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace Modules\Adjustment\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* The module namespace to assume when generating URLs to actions.
*
* @var string
*/
protected $moduleNamespace = 'Modules\Adjustment\Http\Controllers';
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/**
* Define the routes for the application.
*
* @return void
*/
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->moduleNamespace)
->group(module_path('Adjustment', '/Routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->moduleNamespace)
->group(module_path('Adjustment', '/Routes/api.php'));
}
}

View File

View File

View File

@ -0,0 +1,64 @@
@extends('layouts.app')
@section('title', 'Create Adjustment')
@push('page_css')
@livewireStyles
@endpush
@section('breadcrumb')
<ol class="breadcrumb border-0 m-0">
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
<li class="breadcrumb-item"><a href="{{ route('adjustments.index') }}">Adjustments</a></li>
<li class="breadcrumb-item active">Add</li>
</ol>
@endsection
@section('content')
<div class="container-fluid mb-4">
<div class="row">
<div class="col-12">
<livewire:search-product/>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<div class="card">
<div class="card-body">
@include('utils.alerts')
<form action="{{ route('adjustments.store') }}" method="POST">
@csrf
<div class="form-row">
<div class="col-lg-6">
<div class="form-group">
<label for="reference">Reference <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="reference" required>
</div>
</div>
<div class="col-lg-6">
<div class="from-group">
<div class="form-group">
<label for="date">Date <span class="text-danger">*</span></label>
<input type="date" class="form-control" name="date" required>
</div>
</div>
</div>
</div>
<livewire:adjustment.product-table/>
<div class="form-group">
<label for="note">Note (If Needed)</label>
<textarea name="note" id="note" rows="5" class="form-control"></textarea>
</div>
<div class="mt-3">
<button type="submit" class="btn btn-primary">
Create Adjustment <i class="bi bi-check"></i>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,67 @@
@extends('layouts.app')
@section('title', 'Edit Adjustment')
@push('page_css')
@livewireStyles
@endpush
@section('breadcrumb')
<ol class="breadcrumb border-0 m-0">
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
<li class="breadcrumb-item"><a href="{{ route('adjustments.index') }}">Adjustments</a></li>
<li class="breadcrumb-item active">Edit</li>
</ol>
@endsection
@section('content')
<div class="container-fluid mb-4">
<div class="row">
<div class="col-12">
<livewire:search-product/>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<div class="card">
<div class="card-body">
@include('utils.alerts')
<form action="{{ route('adjustments.update', $adjustment) }}" method="POST">
@csrf
@method('patch')
<div class="form-row">
<div class="col-lg-6">
<div class="form-group">
<label for="reference">Reference <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="reference" required value="{{ $adjustment->reference }}">
</div>
</div>
<div class="col-lg-6">
<div class="from-group">
<div class="form-group">
<label for="date">Date <span class="text-danger">*</span></label>
<input type="date" class="form-control" name="date" required value="{{ $adjustment->getAttributes()['date'] }}">
</div>
</div>
</div>
</div>
<livewire:adjustment.product-table :adjustedProducts="$adjustment->adjustedProducts->toArray()"/>
<div class="form-group">
<label for="note">Note (If Needed)</label>
<textarea name="note" id="note" rows="5" class="form-control">
{{ $adjustment->note }}
</textarea>
</div>
<div class="mt-3">
<button type="submit" class="btn btn-primary">
Update Adjustment <i class="bi bi-check"></i>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,40 @@
@extends('layouts.app')
@section('title', 'Adjustments')
@section('third_party_stylesheets')
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.25/css/dataTables.bootstrap4.min.css">
@endsection
@section('breadcrumb')
<ol class="breadcrumb border-0 m-0">
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
<li class="breadcrumb-item active">Adjustments</li>
</ol>
@endsection
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<a href="{{ route('adjustments.create') }}" class="btn btn-primary">
Add Adjustment <i class="bi bi-plus"></i>
</a>
<hr>
<div class="table-responsive">
{!! $dataTable->table() !!}
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('page_scripts')
{!! $dataTable->scripts() !!}
@endpush

View File

@ -0,0 +1,24 @@
@can('edit_adjustments')
<a href="{{ route('adjustments.edit', $data->id) }}" class="btn btn-info btn-sm">
<i class="bi bi-pencil"></i>
</a>
@endcan
@can('show_adjustments')
<a href="{{ route('adjustments.show', $data->id) }}" class="btn btn-primary btn-sm">
<i class="bi bi-eye"></i>
</a>
@endcan
@can('delete_adjustments')
<button id="delete" class="btn btn-danger btn-sm" onclick="
event.preventDefault();
if (confirm('Are you sure? It will delete the data permanently!')) {
document.getElementById('destroy{{ $data->id }}').submit()
}
">
<i class="bi bi-trash"></i>
<form id="destroy{{ $data->id }}" class="d-none" action="{{ route('adjustments.destroy', $data->id) }}" method="POST">
@csrf
@method('delete')
</form>
</button>
@endcan

View File

@ -0,0 +1,70 @@
@extends('layouts.app')
@section('title', 'Adjustment Details')
@push('page_css')
@livewireStyles
@endpush
@section('breadcrumb')
<ol class="breadcrumb border-0 m-0">
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
<li class="breadcrumb-item"><a href="{{ route('adjustments.index') }}">Adjustments</a></li>
<li class="breadcrumb-item active">Details</li>
</ol>
@endsection
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered">
<tr>
<th colspan="2">
Date
</th>
<th colspan="2">
Reference
</th>
</tr>
<tr>
<td colspan="2">
{{ $adjustment->date }}
</td>
<td colspan="2">
{{ $adjustment->reference }}
</td>
</tr>
<tr>
<th>Product Name</th>
<th>Code</th>
<th>Quantity</th>
<th>Type</th>
</tr>
@foreach($adjustment->adjustedProducts as $adjustedProduct)
<tr>
<td>{{ $adjustedProduct->product->product_name }}</td>
<td>{{ $adjustedProduct->product->product_code }}</td>
<td>{{ $adjustedProduct->quantity }}</td>
<td>
@if($adjustedProduct->type == 'add')
(+) Addition
@else
(-) Subtraction
@endif
</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

View File

@ -0,0 +1,18 @@
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/adjustment', function (Request $request) {
return $request->user();
});

View File

@ -0,0 +1,17 @@
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::group(['middleware' => 'auth'], function () {
//Product Adjustment
Route::resource('adjustments', 'AdjustmentController');
});

View File

View File

@ -0,0 +1,23 @@
{
"name": "nwidart/adjustment",
"description": "",
"authors": [
{
"name": "Nicolas Widart",
"email": "n.widart@gmail.com"
}
],
"extra": {
"laravel": {
"providers": [],
"aliases": {
}
}
},
"autoload": {
"psr-4": {
"Modules\\Adjustment\\": ""
}
}
}

View File

@ -0,0 +1,13 @@
{
"name": "Adjustment",
"alias": "adjustment",
"description": "",
"keywords": [],
"priority": 0,
"providers": [
"Modules\\Adjustment\\Providers\\AdjustmentServiceProvider"
],
"aliases": {},
"files": [],
"requires": []
}

View File

@ -0,0 +1,17 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"cross-env": "^7.0",
"laravel-mix": "^5.0.1",
"laravel-mix-merge-manifest": "^0.1.2"
}
}

14
Modules/Adjustment/webpack.mix.js vendored Normal file
View File

@ -0,0 +1,14 @@
const dotenvExpand = require('dotenv-expand');
dotenvExpand(require('dotenv').config({ path: '../../.env'/*, debug: true*/}));
const mix = require('laravel-mix');
require('laravel-mix-merge-manifest');
mix.setPublicPath('../../public').mergeManifest();
mix.js(__dirname + '/Resources/assets/js/app.js', 'js/adjustment.js')
.sass( __dirname + '/Resources/assets/sass/app.scss', 'css/adjustment.css');
if (mix.inProduction()) {
mix.version();
}

View File

@ -17,7 +17,7 @@
<div class="container-fluid">
<div class="row">
<div class="col-12">
<livewire:barcode.search-product/>
<livewire:search-product/>
</div>
</div>

View File

@ -88,7 +88,7 @@
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label for="product_quantity">Staring Quantity <span class="text-danger">*</span></label>
<label for="product_quantity">Quantity <span class="text-danger">*</span></label>
<input type="number" class="form-control" name="product_quantity" required value="{{ old('product_quantity') }}" min="1">
</div>
</div>

View File

@ -88,7 +88,7 @@
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label for="product_quantity">Staring Quantity <span class="text-danger">*</span></label>
<label for="product_quantity">Quantity <span class="text-danger">*</span></label>
<input type="number" class="form-control" name="product_quantity" required value="{{ $product->product_quantity }}" min="1">
</div>
</div>

View File

@ -0,0 +1,101 @@
<?php
namespace App\DataTables;
use Modules\Adjustment\Entities\Adjustment;
use Yajra\DataTables\Html\Button;
use Yajra\DataTables\Html\Column;
use Yajra\DataTables\Html\Editor\Editor;
use Yajra\DataTables\Html\Editor\Fields;
use Yajra\DataTables\Services\DataTable;
class AdjustmentsDataTable extends DataTable
{
/**
* Build DataTable class.
*
* @param mixed $query Results from query() method.
* @return \Yajra\DataTables\DataTableAbstract
*/
public function dataTable($query)
{
return datatables()
->eloquent($query)
->addColumn('action', function ($data) {
return view('adjustment::partials.actions', compact('data'));
});
}
/**
* Get query source of dataTable.
*
* @param \Modules\Adjustment\Entities\Adjustment $model
* @return \Illuminate\Database\Eloquent\Builder
*/
public function query(Adjustment $model)
{
return $model->newQuery()->withCount('adjustedProducts');
}
/**
* Optional method if you want to use html builder.
*
* @return \Yajra\DataTables\Html\Builder
*/
public function html()
{
return $this->builder()
->setTableId('adjustments-table')
->columns($this->getColumns())
->minifiedAjax()
->dom("<'row'<'col-md-3'l><'col-md-5 mb-2'B><'col-md-4'f>> .
'tr' .
<'row'<'col-md-5'i><'col-md-7 mt-2'p>>")
->orderBy(0)
->buttons(
Button::make('excel')
->text('<i class="bi bi-file-earmark-excel-fill"></i> Excel'),
Button::make('print')
->text('<i class="bi bi-printer-fill"></i> Print'),
Button::make('reset')
->text('<i class="bi bi-x-circle"></i> Reset'),
Button::make('reload')
->text('<i class="bi bi-arrow-repeat"></i> Reload')
);
}
/**
* Get columns.
*
* @return array
*/
protected function getColumns()
{
return [
Column::make('date')
->className('text-center align-middle'),
Column::make('reference')
->className('text-center align-middle'),
Column::make('adjusted_products_count')
->title('Products')
->className('text-center align-middle'),
Column::computed('action')
->exportable(false)
->printable(false)
->className('text-center align-middle')
];
}
/**
* Get filename for export.
*
* @return string
*/
protected function filename()
{
return 'Adjustments_' . date('YmdHis');
}
}

View File

@ -80,39 +80,32 @@ class ProductDataTable extends DataTable
return [
Column::computed('product_image')
->title('Image')
->addClass('text-center')
->addClass('align-middle'),
->className('text-center align-middle'),
Column::make('product_name')
->title('Name')
->addClass('text-center')
->addClass('align-middle'),
->className('text-center align-middle'),
Column::make('product_code')
->title('Code')
->addClass('text-center')
->addClass('align-middle'),
->className('text-center align-middle'),
Column::make('product_price')
->title('Price')
->addClass('text-center')
->addClass('align-middle'),
->className('text-center align-middle'),
Column::make('product_quantity')
->title('Quantity')
->addClass('text-center')
->addClass('align-middle'),
->className('text-center align-middle'),
Column::make('category.category_name')
->title('Category')
->addClass('text-center')
->addClass('align-middle'),
->className('text-center align-middle'),
Column::computed('action')
->exportable(false)
->printable(false)
->addClass('text-center')
->addClass('align-middle'),
->className('text-center align-middle')
];
}

View File

@ -0,0 +1,56 @@
<?php
namespace App\Http\Livewire\Adjustment;
use Illuminate\Support\Collection;
use Livewire\Component;
use Modules\Product\Entities\Product;
class ProductTable extends Component
{
protected $listeners = ['productSelected'];
public $products;
public $hasAdjustments;
public function mount($adjustedProducts = null) {
$this->products = [];
if ($adjustedProducts) {
$this->hasAdjustments = true;
$this->products = $adjustedProducts;
} else {
$this->hasAdjustments = false;
}
}
public function render() {
return view('livewire.adjustment.product-table');
}
public function productSelected($product) {
switch ($this->hasAdjustments) {
case true:
if (in_array($product, array_map(function ($adjustment) {
return $adjustment['product'];
}, $this->products))) {
return session()->flash('message', 'Already exists in the product table!');
}
break;
case false:
if (in_array($product, $this->products)) {
return session()->flash('message', 'Already exists in the product table!');
}
break;
default:
return session()->flash('message', 'Something went wrong!');;
}
array_push($this->products, $product);
}
public function removeProduct($key) {
unset($this->products[$key]);
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace App\Http\Livewire\Barcode;
namespace App\Http\Livewire;
use Illuminate\Support\Collection;
use Livewire\Component;
@ -18,7 +18,7 @@ class SearchProduct extends Component
}
public function render() {
return view('livewire.barcode.search-product');
return view('livewire.search-product');
}
public function updatedQuery() {

View File

@ -7,9 +7,11 @@
"require": {
"php": "^7.3|^8.0",
"barryvdh/laravel-snappy": "^0.4.8",
"bumbummen99/shoppingcart": "^4.0",
"fideloper/proxy": "^4.4",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"h4cc/wkhtmltopdf-amd64": "^0.12.4",
"infyomlabs/laravel-ui-coreui": "^3.0",
"laravel/framework": "^8.40",
"laravel/tinker": "^2.5",
@ -62,7 +64,10 @@
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
"sort-packages": true,
"platform": {
"php": "7.4"
}
},
"minimum-stability": "dev",
"prefer-stable": true

127
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "53badb2a324d9caf89ceed410177f49b",
"content-hash": "81cce1ef2defb71f07fb109dda14cc2c",
"packages": [
{
"name": "asm89/stack-cors",
@ -183,6 +183,77 @@
],
"time": "2021-01-20T22:51:39+00:00"
},
{
"name": "bumbummen99/shoppingcart",
"version": "4.0.0",
"source": {
"type": "git",
"url": "https://github.com/bumbummen99/LaravelShoppingcart.git",
"reference": "f2c763e311b0fc58b2dfdfa2c41c6318a7c884e1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bumbummen99/LaravelShoppingcart/zipball/f2c763e311b0fc58b2dfdfa2c41c6318a7c884e1",
"reference": "f2c763e311b0fc58b2dfdfa2c41c6318a7c884e1",
"shasum": ""
},
"require": {
"illuminate/events": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0||^8.0",
"illuminate/session": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0||^8.0",
"illuminate/support": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0||^8.0",
"nesbot/carbon": "~1.20||^1.26.3||^2.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^3.4|^4.0|^5.0|^6.0",
"phpunit/phpunit": "~5.0||~6.0||~7.0||~8.0||~9.0"
},
"suggest": {
"gloudemans/notify": "Simple flash notifications for Laravel"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Gloudemans\\Shoppingcart\\ShoppingcartServiceProvider"
],
"aliases": {
"Cart": "Gloudemans\\Shoppingcart\\Facades\\Cart"
}
}
},
"autoload": {
"psr-4": {
"Gloudemans\\Shoppingcart\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Gloudemans",
"email": "info@robgloudemans.nl",
"homepage": "http://robgloudemans.nl/"
},
{
"name": "Patrick Henninger",
"email": "privat@skyraptor.eu",
"homepage": "https://skyraptor.eu/"
}
],
"description": "Laravel Shoppingcart",
"keywords": [
"laravel",
"shoppingcart"
],
"support": {
"issues": "https://github.com/bumbummen99/LaravelShoppingcart/issues",
"source": "https://github.com/bumbummen99/LaravelShoppingcart/tree/4.0.0"
},
"time": "2021-01-18T23:55:30+00:00"
},
{
"name": "doctrine/inflector",
"version": "2.0.3",
@ -980,6 +1051,55 @@
},
"time": "2021-06-30T20:03:07+00:00"
},
{
"name": "h4cc/wkhtmltopdf-amd64",
"version": "0.12.4",
"source": {
"type": "git",
"url": "https://github.com/h4cc/wkhtmltopdf-amd64.git",
"reference": "4e2ab2d032a5d7fbe2a741de8b10b8989523c95b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/h4cc/wkhtmltopdf-amd64/zipball/4e2ab2d032a5d7fbe2a741de8b10b8989523c95b",
"reference": "4e2ab2d032a5d7fbe2a741de8b10b8989523c95b",
"shasum": ""
},
"bin": [
"bin/wkhtmltopdf-amd64"
],
"type": "library",
"autoload": {
"psr-4": {
"h4cc\\WKHTMLToPDF\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL Version 3"
],
"authors": [
{
"name": "Julius Beckmann",
"email": "github@h4cc.de"
}
],
"description": "Convert html to pdf using webkit (qtwebkit). Static linked linux binary for amd64 systems.",
"homepage": "http://wkhtmltopdf.org/",
"keywords": [
"binary",
"convert",
"pdf",
"snapshot",
"thumbnail",
"wkhtmltopdf"
],
"support": {
"issues": "https://github.com/h4cc/wkhtmltopdf-amd64/issues",
"source": "https://github.com/h4cc/wkhtmltopdf-amd64/tree/master"
},
"time": "2018-01-15T06:57:33+00:00"
},
{
"name": "infyomlabs/laravel-generator-helpers",
"version": "v3.0.0",
@ -9611,5 +9731,8 @@
"php": "^7.3|^8.0"
},
"platform-dev": [],
"plugin-api-version": "2.0.0"
"platform-overrides": {
"php": "7.4"
},
"plugin-api-version": "2.1.0"
}

79
config/cart.php Normal file
View File

@ -0,0 +1,79 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Gross price as base price
|--------------------------------------------------------------------------
|
| This default value is used to select the method to calculate prices and taxes
| If true the item price is managed as a gross price, so taxes will be calculated by separation/exclusion
|
*/
'calculator' => \Gloudemans\Shoppingcart\Calculation\DefaultCalculator::class,
/*
|--------------------------------------------------------------------------
| Default tax rate
|--------------------------------------------------------------------------
|
| This default tax rate will be used when you make a class implement the
| Taxable interface and use the HasTax trait.
|
*/
'tax' => 0,
/*
|--------------------------------------------------------------------------
| Shoppingcart database settings
|--------------------------------------------------------------------------
|
| Here you can set the connection that the shoppingcart should use when
| storing and restoring a cart.
|
*/
'database' => [
'connection' => null,
'table' => 'shoppingcart',
],
/*
|--------------------------------------------------------------------------
| Destroy the cart on user logout
|--------------------------------------------------------------------------
|
| When this option is set to 'true' the cart will automatically
| destroy all cart instances when the user logs out.
|
*/
'destroy_on_logout' => true,
/*
|--------------------------------------------------------------------------
| Default number format
|--------------------------------------------------------------------------
|
| This defaults will be used for the formatted numbers if you don't
| set them in the method call.
|
*/
'format' => [
'decimals' => 2,
'decimal_point' => '.',
'thousand_separator' => ',',
],
];

View File

@ -35,10 +35,11 @@ return [
'pdf' => [
'enabled' => true,
'binary' => env('WKHTML_PDF_BINARY', '"C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe"'),
'binary' => env('WKHTML_PDF_BINARY', base_path('vendor/h4cc/wkhtmltopdf-amd64/bin/wkhtmltopdf-amd64'),),
'timeout' => false,
'options' => [
'enable-local-file-access' => true
'enable-local-file-access' => true,
'print-media-type' => true
],
'env' => [],
],

View File

@ -1,5 +1,6 @@
{
"Product": true,
"Upload": true,
"User": true
"User": true,
"Adjustment": true
}

9
public/css/app.css vendored
View File

@ -18781,5 +18781,12 @@ h3 {
}
table {
width: 100% !important;
width: 990px !important;
}
/* Large devices (desktops, 992px and up) */
@media (min-width: 992px) {
table {
width: 100% !important;
}
}

View File

@ -8,5 +8,12 @@
}
table {
width: 100% !important;
width: 990px !important;
}
/* Large devices (desktops, 992px and up) */
@media (min-width: 992px) {
table {
width: 100% !important;
}
}

View File

@ -40,6 +40,28 @@
</li>
@endcan
@can('access_adjustments')
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown {{ request()->routeIs('adjustments.*') ? 'c-show' : '' }}">
<a class="c-sidebar-nav-link c-sidebar-nav-dropdown-toggle" href="#">
<i class="c-sidebar-nav-icon bi bi-pencil-square" style="line-height: 1;"></i> Adjustments
</a>
<ul class="c-sidebar-nav-dropdown-items">
@can('create_adjustments')
<li class="c-sidebar-nav-item">
<a class="c-sidebar-nav-link {{ request()->routeIs('adjustments.create') ? 'c-active' : '' }}" href="{{ route('adjustments.create') }}">
<i class="c-sidebar-nav-icon bi bi-journal-plus" style="line-height: 1;"></i> Create Adjustment
</a>
</li>
@endcan
<li class="c-sidebar-nav-item">
<a class="c-sidebar-nav-link {{ request()->routeIs('adjustments.index') ? 'c-active' : '' }}" href="{{ route('adjustments.index') }}">
<i class="c-sidebar-nav-icon bi bi-journals" style="line-height: 1;"></i> All Adjustments
</a>
</li>
</ul>
</li>
@endcan
@can('access_user_management')
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown {{ request()->routeIs('roles*') ? 'c-show' : '' }}">
<a class="c-sidebar-nav-link c-sidebar-nav-dropdown-toggle" href="#">

View File

@ -0,0 +1,78 @@
<div>
@if (session()->has('message'))
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<div class="alert-body">
<span>{{ session('message') }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
@endif
<div class="table-responsive-md">
<table class="table table-bordered">
<thead>
<tr class="align-middle">
<th class="align-middle">#</th>
<th class="align-middle">Product Name</th>
<th class="align-middle">Code</th>
<th class="align-middle">Stock</th>
<th class="align-middle">Quantity</th>
<th class="align-middle">Type</th>
<th class="align-middle">Action</th>
</tr>
</thead>
<tbody>
@if(!empty($products))
@foreach($products as $key => $product)
<tr>
<td class="align-middle">{{ $key + 1 }}</td>
<td class="align-middle">{{ $product['product_name'] ?? $product['product']['product_name'] }}</td>
<td class="align-middle">{{ $product['product_code'] ?? $product['product']['product_code'] }}</td>
<td class="align-middle text-center">
<span class="badge badge-info">
{{ $product['product_quantity'] ?? $product['product']['product_quantity'] }}
</span>
</td>
<input type="hidden" name="product_ids[]" value="{{ $product['product']['id'] ?? $product['id'] }}">
<td class="align-middle">
<input type="number" name="quantities[]" min="1" class="form-control" value="{{ $product['quantity'] ?? 1 }}">
</td>
<td class="align-middle">
@if(isset($product['type']))
@if($product['type'] == 'add')
<select name="types[]" class="form-control">
<option value="add">(+) Addition</option>
</select>
@elseif($product['type'] == 'sub')
<select name="types[]" class="form-control">
<option value="sub">(-) Subtraction</option>
</select>
@endif
@else
<select name="types[]" class="form-control">
<option value="add">(+) Addition</option>
<option value="sub">(-) Subtraction</option>
</select>
@endif
</td>
<td class="align-middle text-center">
<button type="button" class="btn btn-danger" wire:click="removeProduct({{ $key }})">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
@endforeach
@else
<tr>
<td colspan="7" class="text-center">
<span class="text-danger">
Please search & select products!
</span>
</td>
</tr>
@endif
</tbody>
</table>
</div>
</div>

View File

@ -48,7 +48,7 @@
<div class="card-body">
<div class="row justify-content-center">
@foreach($barcodes as $barcode)
<div class="col-lg-3 col-md-4 col-6" style="border: 1px solid #ffffff;border-style: dashed;background-color: #48FCFE;">
<div class="col-lg-3 col-md-4 col-sm-6" style="border: 1px solid #ffffff;border-style: dashed;background-color: #48FCFE;">
<p class="mt-3 mb-1" style="font-size: 15px;color: #000;">
{{ $product->product_name }}
</p>

View File

@ -32,7 +32,7 @@
<ul class="list-group list-group-flush">
@foreach($searchResults as $result)
<li class="list-group-item list-group-item-action">
<a wire:click="resetQuery" wire:click.prevent="selectProduct({{$result}})" href="#">
<a wire:click="resetQuery" wire:click.prevent="selectProduct({{ $result }})" href="#">
{{ $result->product_name }} | {{ $result->product_code }}
</a>
</li>