firstcommit
This commit is contained in:
0
Modules/Estimate/app/Http/Controllers/.gitkeep
Normal file
0
Modules/Estimate/app/Http/Controllers/.gitkeep
Normal file
194
Modules/Estimate/app/Http/Controllers/EstimateController.php
Normal file
194
Modules/Estimate/app/Http/Controllers/EstimateController.php
Normal file
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Billing\Repositories\BillingComponentInterface;
|
||||
use Modules\Customer\Repositories\CustomerInterface;
|
||||
use Modules\Estimate\Models\Estimate;
|
||||
use Modules\Estimate\Models\EstimateDetail;
|
||||
use Modules\Estimate\Repositories\EstimateDetailInterface;
|
||||
use Modules\Estimate\Repositories\EstimateInterface;
|
||||
|
||||
class EstimateController extends Controller
|
||||
{
|
||||
private $estimateRepository;
|
||||
private $estimateDetailRepository;
|
||||
private $customerRepository;
|
||||
private $billingComponentRepository;
|
||||
|
||||
public function __construct(
|
||||
EstimateInterface $estimateRepository,
|
||||
BillingComponentInterface $billingComponentRepository,
|
||||
CustomerInterface $customerRepository
|
||||
) {
|
||||
$this->estimateRepository = $estimateRepository;
|
||||
$this->customerRepository = $customerRepository;
|
||||
$this->billingComponentRepository = $billingComponentRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data['title'] = 'Estimate List';
|
||||
$data['statusList'] = Estimate::APPROVAL_STATUS;
|
||||
$data['estimates'] = $this->estimateRepository->findAll();
|
||||
return view('estimate::estimate.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$data['title'] = 'Create Estimate';
|
||||
$data['editable'] = false;
|
||||
$data['customerList'] = $this->customerRepository->pluck();
|
||||
$data['billingComponentList'] = $this->billingComponentRepository->pluck();
|
||||
return view('estimate::estimate.create', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
|
||||
$request->merge([
|
||||
'approval_status' => 1,
|
||||
]);
|
||||
|
||||
try {
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
$estimateData = $request->only(Estimate::getFillableFields());
|
||||
|
||||
$estimate = $this->estimateRepository->create($estimateData);
|
||||
|
||||
$estimateDetails = json_decode($request->estimateDetails);
|
||||
|
||||
foreach ($estimateDetails as $item) {
|
||||
|
||||
$estimateDetailData = [
|
||||
'estimate_id' => $estimate->id,
|
||||
'billing_component_id' => $item->billing_component_id,
|
||||
'price' => $item->price,
|
||||
'qty' => $item->qty,
|
||||
'tax_amount' => $item->tax_amount,
|
||||
];
|
||||
|
||||
EstimateDetail::create($estimateDetailData);
|
||||
|
||||
}
|
||||
|
||||
toastr()->success('Estimate created succesfully');
|
||||
|
||||
DB::commit();
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
|
||||
echo $th->getMessage();
|
||||
|
||||
DB::rollback();
|
||||
|
||||
toastr()->error($th->getMessage());
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('estimate.index');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the specified resource.
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$data['title'] = 'Show Estimate';
|
||||
$data['estimate'] = $this->estimateRepository->getEstimateById($id);
|
||||
$data['statusList'] = Estimate::APPROVAL_STATUS;
|
||||
$data['customerList'] = $this->customerRepository->pluck();
|
||||
return view('estimate::estimate.show', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$data['title'] = 'Edit Estimate';
|
||||
$data['editable'] = true;
|
||||
$data['estimate'] = $this->estimateRepository->getEstimateById($id);
|
||||
$data['customerList'] = $this->customerRepository->pluck();
|
||||
$data['billingComponentList'] = $this->billingComponentRepository->pluck();
|
||||
return view('estimate::estimate.edit', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, $id): RedirectResponse
|
||||
{
|
||||
$estimateData = $request->only(Estimate::getFillableFields());
|
||||
|
||||
try {
|
||||
|
||||
$this->estimateRepository->update($id, $estimateData);
|
||||
|
||||
flash()->success('Estimate updated succesfully');
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
|
||||
echo $th->getMessage();
|
||||
|
||||
flash()->error($th->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('estimate.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
try {
|
||||
$EstimateModel = $this->estimateRepository->getEstimateById($id);
|
||||
|
||||
$EstimateModel->delete();
|
||||
|
||||
flash()->success('Estimate deleted succesfully');
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
|
||||
flash()->error($th->getMessage());
|
||||
}
|
||||
|
||||
return response()->json(['status' => true, 'message' => 'Estimate deleted succesfully']);
|
||||
|
||||
}
|
||||
|
||||
public function changeStatus(Request $request)
|
||||
{
|
||||
try {
|
||||
$estimate = $this->estimateRepository->getEstimateById($request->estimate_id);
|
||||
$estimate->update([
|
||||
'approval_status' => $request->approval_status,
|
||||
'approval_remarks' => $request->approval_remarks,
|
||||
'approval_on' => now(),
|
||||
'approval_by' => auth()->user()->id,
|
||||
]);
|
||||
|
||||
return redirect()->back();
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
flash()->error($th->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Customer\Repositories\CustomerInterface;
|
||||
use Modules\Estimate\Models\EstimateDetail;
|
||||
use Modules\Estimate\Repositories\EstimateDetailInterface;
|
||||
|
||||
class EstimateDetailController extends Controller
|
||||
{
|
||||
private $estimateDetailRepository;
|
||||
private $customerRepository;
|
||||
|
||||
public function __construct(
|
||||
EstimateDetailInterface $estimateDetailRepository,
|
||||
CustomerInterface $customerRepository
|
||||
) {
|
||||
$this->estimateDetailRepository = $estimateDetailRepository;
|
||||
$this->customerRepository = $customerRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data['title'] = 'Estimate Detail List';
|
||||
$data['estimateDetails'] = $this->estimateDetailRepository->findAll();
|
||||
return view('estimate::estimateDetail.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$data['title'] = 'Create EstimateDetail';
|
||||
$data['editable'] = false;
|
||||
$data['customerList'] = $this->customerRepository->pluck();
|
||||
return view('estimate::estimateDetail.create', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
try {
|
||||
|
||||
$inputData = $request->all();
|
||||
|
||||
$this->estimateDetailRepository->create($inputData);
|
||||
|
||||
toastr()->success('EstimateDetail created succesfully');
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
|
||||
toastr()->error($th->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('estimateDetail.index');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the specified resource.
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$data['title'] = 'Show EstimateDetail';
|
||||
$data['estimateDetail'] = $this->estimateDetailRepository->getEstimateDetailById($id);
|
||||
$data['customerList'] = $this->customerRepository->pluck();
|
||||
return view('estimate::estimateDetail.show', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$data['title'] = 'Edit EstimateDetail';
|
||||
$data['editable'] = true;
|
||||
$data['estimateDetail'] = $this->estimateDetailRepository->getEstimateDetailById($id);
|
||||
return view('estimate::estimateDetail.edit', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, $id): RedirectResponse
|
||||
{
|
||||
$inputData = $request->except(['_method', '_token']);
|
||||
try {
|
||||
|
||||
$this->estimateDetailRepository->update($id, $inputData);
|
||||
|
||||
flash()->success('EstimateDetail updated succesfully');
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
|
||||
flash()->error($th->getMessage());
|
||||
}
|
||||
return redirect()->route('EstimateDetail.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
try {
|
||||
$EstimateDetailModel = $this->estimateDetailRepository->getEstimateDetailById($id);
|
||||
|
||||
$EstimateDetailModel->delete();
|
||||
|
||||
flash()->success('EstimateDetail deleted succesfully');
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
|
||||
flash()->error($th->getMessage());
|
||||
}
|
||||
|
||||
return response()->json(['status' => true, 'message' => 'EstimateDetail deleted succesfully']);
|
||||
|
||||
}
|
||||
}
|
0
Modules/Estimate/app/Http/Requests/.gitkeep
Normal file
0
Modules/Estimate/app/Http/Requests/.gitkeep
Normal file
0
Modules/Estimate/app/Models/.gitkeep
Normal file
0
Modules/Estimate/app/Models/.gitkeep
Normal file
70
Modules/Estimate/app/Models/Estimate.php
Normal file
70
Modules/Estimate/app/Models/Estimate.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Models;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Modules\Customer\Models\Customer;
|
||||
|
||||
class Estimate extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = "tbl_estimates";
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'estimate_no',
|
||||
'date',
|
||||
'customer_id',
|
||||
'fiscal_year_id',
|
||||
'validity',
|
||||
'conditions',
|
||||
'approval_status',
|
||||
'approval_by',
|
||||
'approval_remarks',
|
||||
'approval_on',
|
||||
'description',
|
||||
'createdBy',
|
||||
'updatedBy',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'date' => 'datetime',
|
||||
'validity' => 'datetime',
|
||||
'approval_on' => 'datetime',
|
||||
'estimate_no' => 'integer',
|
||||
];
|
||||
|
||||
const APPROVAL_STATUS = [
|
||||
1 => 'Pending',
|
||||
2 => 'Approved',
|
||||
3 => 'Rejected',
|
||||
];
|
||||
|
||||
public function customer()
|
||||
{
|
||||
return $this->belongsTo(Customer::class, 'customer_id');
|
||||
}
|
||||
|
||||
public function approver()
|
||||
{
|
||||
return $this->belongsTo(Customer::class, 'approval_by');
|
||||
}
|
||||
|
||||
public function estimateDetails()
|
||||
{
|
||||
return $this->hasMany(EstimateDetail::class, 'estimate_id');
|
||||
}
|
||||
|
||||
public static function getFillableFields()
|
||||
{
|
||||
return (new self())->fillable;
|
||||
}
|
||||
}
|
48
Modules/Estimate/app/Models/EstimateDetail.php
Normal file
48
Modules/Estimate/app/Models/EstimateDetail.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Modules\Billing\Models\BillingComponent;
|
||||
use Modules\Estimate\Database\Factories\EstimateDetailFactory;
|
||||
|
||||
class EstimateDetail extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = "tbl_estimate_details";
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'estimate_id',
|
||||
'billing_component_id',
|
||||
'fiscal_year_id',
|
||||
'price',
|
||||
'qty',
|
||||
'tax_amount',
|
||||
'createdBy',
|
||||
'updatedBy',
|
||||
'remarks',
|
||||
'status',
|
||||
];
|
||||
|
||||
|
||||
public function estimate()
|
||||
{
|
||||
return $this->belongsTo(Estimate::class, 'estimate_id')->withDefault();
|
||||
}
|
||||
|
||||
public function billingComponent()
|
||||
{
|
||||
return $this->belongsTo(BillingComponent::class, 'billing_component_id')->withDefault();
|
||||
}
|
||||
|
||||
public function getFillableFields()
|
||||
{
|
||||
return (new self())->fillable;
|
||||
}
|
||||
}
|
0
Modules/Estimate/app/Observers/.gitkeep
Normal file
0
Modules/Estimate/app/Observers/.gitkeep
Normal file
0
Modules/Estimate/app/Providers/.gitkeep
Normal file
0
Modules/Estimate/app/Providers/.gitkeep
Normal file
126
Modules/Estimate/app/Providers/EstimateServiceProvider.php
Normal file
126
Modules/Estimate/app/Providers/EstimateServiceProvider.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Modules\Estimate\Repositories\EstimateDetailInterface;
|
||||
use Modules\Estimate\Repositories\EstimateDetailRepository;
|
||||
use Modules\Estimate\Repositories\EstimateInterface;
|
||||
use Modules\Estimate\Repositories\EstimateRepository;
|
||||
|
||||
class EstimateServiceProvider extends ServiceProvider
|
||||
{
|
||||
protected string $moduleName = 'Estimate';
|
||||
|
||||
protected string $moduleNameLower = 'estimate';
|
||||
|
||||
/**
|
||||
* Boot the application events.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->registerCommands();
|
||||
$this->registerCommandSchedules();
|
||||
$this->registerTranslations();
|
||||
$this->registerConfig();
|
||||
$this->registerViews();
|
||||
$this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->bind(EstimateInterface::class, EstimateRepository::class);
|
||||
$this->app->bind(EstimateDetailInterface::class, EstimateDetailRepository::class);
|
||||
$this->app->register(EventServiceProvider::class);
|
||||
$this->app->register(RouteServiceProvider::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register commands in the format of Command::class
|
||||
*/
|
||||
protected function registerCommands(): void
|
||||
{
|
||||
// $this->commands([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register command Schedules.
|
||||
*/
|
||||
protected function registerCommandSchedules(): void
|
||||
{
|
||||
// $this->app->booted(function () {
|
||||
// $schedule = $this->app->make(Schedule::class);
|
||||
// $schedule->command('inspire')->hourly();
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
*/
|
||||
public function registerTranslations(): void
|
||||
{
|
||||
$langPath = resource_path('lang/modules/'.$this->moduleNameLower);
|
||||
|
||||
if (is_dir($langPath)) {
|
||||
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
|
||||
$this->loadJsonTranslationsFrom($langPath);
|
||||
} else {
|
||||
$this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
|
||||
$this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*/
|
||||
protected function registerConfig(): void
|
||||
{
|
||||
$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.
|
||||
*/
|
||||
public function registerViews(): void
|
||||
{
|
||||
$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);
|
||||
|
||||
$componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.ltrim(config('modules.paths.generator.component-class.path'), config('modules.paths.app_folder', '')));
|
||||
Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function provides(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string>
|
||||
*/
|
||||
private function getPublishableViewPaths(): array
|
||||
{
|
||||
$paths = [];
|
||||
foreach (config('view.paths') as $path) {
|
||||
if (is_dir($path.'/modules/'.$this->moduleNameLower)) {
|
||||
$paths[] = $path.'/modules/'.$this->moduleNameLower;
|
||||
}
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
}
|
32
Modules/Estimate/app/Providers/EventServiceProvider.php
Normal file
32
Modules/Estimate/app/Providers/EventServiceProvider.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event handler mappings for the application.
|
||||
*
|
||||
* @var array<string, array<int, string>>
|
||||
*/
|
||||
protected $listen = [];
|
||||
|
||||
/**
|
||||
* Indicates if events should be discovered.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $shouldDiscoverEvents = true;
|
||||
|
||||
/**
|
||||
* Configure the proper event listeners for email verification.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configureEmailVerification(): void
|
||||
{
|
||||
|
||||
}
|
||||
}
|
49
Modules/Estimate/app/Providers/RouteServiceProvider.php
Normal file
49
Modules/Estimate/app/Providers/RouteServiceProvider.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Called before routes are registered.
|
||||
*
|
||||
* Register any model bindings or pattern based filters.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*/
|
||||
public function map(): void
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
$this->mapWebRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "web" routes for the application.
|
||||
*
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
*/
|
||||
protected function mapWebRoutes(): void
|
||||
{
|
||||
Route::middleware('web')->group(module_path('Estimate', '/routes/web.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "api" routes for the application.
|
||||
*
|
||||
* These routes are typically stateless.
|
||||
*/
|
||||
protected function mapApiRoutes(): void
|
||||
{
|
||||
Route::middleware('api')->prefix('api')->name('api.')->group(module_path('Estimate', '/routes/api.php'));
|
||||
}
|
||||
}
|
0
Modules/Estimate/app/Repositories/.gitkeep
Normal file
0
Modules/Estimate/app/Repositories/.gitkeep
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Repositories;
|
||||
|
||||
interface EstimateDetailInterface
|
||||
{
|
||||
public function findAll();
|
||||
public function getEstimateDetailById($EstimateDetailId);
|
||||
public function delete($EstimateDetailId);
|
||||
public function create($EstimateDetailDetails);
|
||||
public function update($EstimateDetailId, array $newDetails);
|
||||
public function pluck();
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Repositories;
|
||||
|
||||
use Modules\Estimate\Models\EstimateDetail;
|
||||
|
||||
class EstimateDetailRepository implements EstimateDetailInterface
|
||||
{
|
||||
public function findAll()
|
||||
{
|
||||
return EstimateDetail::all();
|
||||
}
|
||||
|
||||
public function getEstimateDetailById($EstimateDetailId)
|
||||
{
|
||||
return EstimateDetail::findOrFail($EstimateDetailId);
|
||||
}
|
||||
|
||||
|
||||
public function delete($EstimateDetailId)
|
||||
{
|
||||
EstimateDetail::destroy($EstimateDetailId);
|
||||
}
|
||||
|
||||
public function create($EstimateDetailDetails)
|
||||
{
|
||||
return EstimateDetail::create($EstimateDetailDetails);
|
||||
}
|
||||
|
||||
public function update($EstimateDetailId, array $newDetails)
|
||||
{
|
||||
return EstimateDetail::whereId($EstimateDetailId)->update($newDetails);
|
||||
}
|
||||
|
||||
public function pluck()
|
||||
{
|
||||
return EstimateDetail::pluck('title', 'id');
|
||||
}
|
||||
|
||||
}
|
14
Modules/Estimate/app/Repositories/EstimateInterface.php
Normal file
14
Modules/Estimate/app/Repositories/EstimateInterface.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Repositories;
|
||||
|
||||
interface EstimateInterface
|
||||
{
|
||||
public function findAll();
|
||||
public function getEstimateById($EstimateId);
|
||||
public function delete($EstimateId);
|
||||
public function create($EstimateDetails);
|
||||
public function update($EstimateId, array $newDetails);
|
||||
public function pluck();
|
||||
|
||||
}
|
40
Modules/Estimate/app/Repositories/EstimateRepository.php
Normal file
40
Modules/Estimate/app/Repositories/EstimateRepository.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Repositories;
|
||||
|
||||
use Modules\Estimate\Models\Estimate;
|
||||
|
||||
class EstimateRepository implements EstimateInterface
|
||||
{
|
||||
public function findAll()
|
||||
{
|
||||
return Estimate::all();
|
||||
}
|
||||
|
||||
public function getEstimateById($EstimateId)
|
||||
{
|
||||
return Estimate::with('estimateDetails')->findOrFail($EstimateId);
|
||||
}
|
||||
|
||||
|
||||
public function delete($EstimateId)
|
||||
{
|
||||
Estimate::destroy($EstimateId);
|
||||
}
|
||||
|
||||
public function create($EstimateDetails)
|
||||
{
|
||||
return Estimate::create($EstimateDetails);
|
||||
}
|
||||
|
||||
public function update($EstimateId, array $newDetails)
|
||||
{
|
||||
return Estimate::whereId($EstimateId)->update($newDetails);
|
||||
}
|
||||
|
||||
public function pluck()
|
||||
{
|
||||
return Estimate::pluck('title', 'id');
|
||||
}
|
||||
|
||||
}
|
30
Modules/Estimate/composer.json
Normal file
30
Modules/Estimate/composer.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "nwidart/estimate",
|
||||
"description": "",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Widart",
|
||||
"email": "n.widart@gmail.com"
|
||||
}
|
||||
],
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [],
|
||||
"aliases": {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Estimate\\": "app/",
|
||||
"Modules\\Estimate\\Database\\Factories\\": "database/factories/",
|
||||
"Modules\\Estimate\\Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Modules\\Estimate\\Tests\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
0
Modules/Estimate/config/.gitkeep
Normal file
0
Modules/Estimate/config/.gitkeep
Normal file
5
Modules/Estimate/config/config.php
Normal file
5
Modules/Estimate/config/config.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'name' => 'Estimate',
|
||||
];
|
0
Modules/Estimate/database/factories/.gitkeep
Normal file
0
Modules/Estimate/database/factories/.gitkeep
Normal file
0
Modules/Estimate/database/migrations/.gitkeep
Normal file
0
Modules/Estimate/database/migrations/.gitkeep
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tbl_estimates', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('estimate_no');
|
||||
$table->string('title')->nullable();
|
||||
$table->dateTime('date')->nullable();
|
||||
$table->unsignedBigInteger('customer_id')->nullable();
|
||||
$table->unsignedBigInteger('fiscal_year_id')->nullable();
|
||||
$table->dateTime('validity')->nullable();
|
||||
$table->text('conditions')->nullable();
|
||||
$table->integer('approval_status')->nullable();
|
||||
$table->unsignedBigInteger('approval_by')->nullable();
|
||||
$table->text('approval_remarks')->nullable();
|
||||
$table->dateTime('approval_on')->nullable();
|
||||
$table->longText('description')->nullable();
|
||||
$table->integer('status')->default(11);
|
||||
$table->unsignedBigInteger('createdBy')->nullable();
|
||||
$table->unsignedBigInteger('updatedBy')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tbl_estimates');
|
||||
}
|
||||
};
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tbl_estimate_details', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title')->nullable();
|
||||
$table->unsignedBigInteger('estimate_id')->nullable();
|
||||
$table->unsignedBigInteger('billing_component_id')->nullable();
|
||||
$table->unsignedBigInteger('fiscal_year_id')->nullable();
|
||||
$table->decimal('price', 10, 2)->nullable();
|
||||
$table->integer('qty')->nullable();
|
||||
$table->decimal('tax_amount', 10, 2)->nullable();
|
||||
$table->unsignedBigInteger('createdBy')->nullable();
|
||||
$table->unsignedBigInteger('updatedBy')->nullable();
|
||||
$table->text('remarks')->nullable();
|
||||
$table->integer('status')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tbl_estimate_details');
|
||||
}
|
||||
};
|
0
Modules/Estimate/database/seeders/.gitkeep
Normal file
0
Modules/Estimate/database/seeders/.gitkeep
Normal file
16
Modules/Estimate/database/seeders/EstimateDatabaseSeeder.php
Normal file
16
Modules/Estimate/database/seeders/EstimateDatabaseSeeder.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Estimate\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class EstimateDatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// $this->call([]);
|
||||
}
|
||||
}
|
11
Modules/Estimate/module.json
Normal file
11
Modules/Estimate/module.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Estimate",
|
||||
"alias": "estimate",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"priority": 0,
|
||||
"providers": [
|
||||
"Modules\\Estimate\\Providers\\EstimateServiceProvider"
|
||||
],
|
||||
"files": []
|
||||
}
|
15
Modules/Estimate/package.json
Normal file
15
Modules/Estimate/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^1.1.2",
|
||||
"laravel-vite-plugin": "^0.7.5",
|
||||
"sass": "^1.69.5",
|
||||
"postcss": "^8.3.7",
|
||||
"vite": "^4.0.0"
|
||||
}
|
||||
}
|
0
Modules/Estimate/resources/assets/.gitkeep
Normal file
0
Modules/Estimate/resources/assets/.gitkeep
Normal file
0
Modules/Estimate/resources/assets/js/app.js
Normal file
0
Modules/Estimate/resources/assets/js/app.js
Normal file
0
Modules/Estimate/resources/assets/sass/app.scss
Normal file
0
Modules/Estimate/resources/assets/sass/app.scss
Normal file
0
Modules/Estimate/resources/views/.gitkeep
Normal file
0
Modules/Estimate/resources/views/.gitkeep
Normal file
16
Modules/Estimate/resources/views/estimate/create.blade.php
Normal file
16
Modules/Estimate/resources/views/estimate/create.blade.php
Normal file
@ -0,0 +1,16 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="page-content">
|
||||
<div class="container-fluid">
|
||||
@include('layouts.partials.breadcrumb', ['title' => $title])
|
||||
|
||||
{{ html()->form('POST')->id('storeUpdateForm')->route('estimate.store')->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
|
||||
|
||||
@include('estimate::estimate.partials.action')
|
||||
|
||||
{{ html()->form()->close() }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
27
Modules/Estimate/resources/views/estimate/edit.blade.php
Normal file
27
Modules/Estimate/resources/views/estimate/edit.blade.php
Normal file
@ -0,0 +1,27 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="page-content">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- start page title -->
|
||||
|
||||
@include('layouts.partials.breadcrumb', ['title' => $title])
|
||||
|
||||
<!-- end page title -->
|
||||
|
||||
{{ html()->modelForm($estimate, 'PUT')->id('storeUpdateForm')->route('estimate.update', $estimate->id)->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
|
||||
|
||||
@include('estimate::estimate.partials.action')
|
||||
|
||||
{{ html()->closeModelForm() }}
|
||||
<!--end row-->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- container-fluid -->
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
82
Modules/Estimate/resources/views/estimate/index.blade.php
Normal file
82
Modules/Estimate/resources/views/estimate/index.blade.php
Normal file
@ -0,0 +1,82 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="page-content">
|
||||
<div class="container-fluid">
|
||||
@include('layouts.partials.breadcrumb', ['title' => $title])
|
||||
|
||||
|
||||
<div class="mb-2 text-end">
|
||||
@can('estimate.create')
|
||||
<a href="{{ route('estimate.create') }}" class="btn btn-success btn-md waves-effect waves-light"><i
|
||||
class="ri-add-fill me-1 align-bottom"></i> Add</a>
|
||||
@endcan
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="buttons-datatables" class="display table-sm table-bordered table"
|
||||
style="width:100%">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>S.N</th>
|
||||
<th>Title</th>
|
||||
<th>Date</th>
|
||||
<th>Validity</th>
|
||||
<th>Customer</th>
|
||||
<th>Approver</th>
|
||||
<th>Approved on</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($estimates as $key => $estimate)
|
||||
<tr>
|
||||
<td>{{ $key + 1 }}</td>
|
||||
<td>{{ $estimate->title }}</td>
|
||||
<td>{{ $estimate->date?->format('Y-m-d') }}</td>
|
||||
<td>{{ $estimate->validity?->format('Y-m-d') }}</td>
|
||||
<td>{{ $estimate->customer?->customer_name }}</td>
|
||||
<td>{{ $estimate->approver?->customer_name ?? 'N/A' }}</td>
|
||||
<td>{{ $estimate->approval_on?->format('Y-m-d') ?? 'N/A' }}</td>
|
||||
<td>{{ $statusList[$estimate->approval_status] }}</td>
|
||||
<td>
|
||||
<div class="hstack flex-wrap gap-3">
|
||||
@can('estimate.show')
|
||||
<a href="{{ route('estimate.show', $estimate->id) }}"
|
||||
class="link-info fs-15">
|
||||
<i class="ri-eye-line"></i>
|
||||
</a>
|
||||
@endcan
|
||||
@can('estimate.edit')
|
||||
<a href="{{ route('estimate.edit', $estimate->id) }}"
|
||||
class="link-success fs-15 edit-item-btn"><i
|
||||
class="ri-edit-2-line"></i></a>
|
||||
@endcan
|
||||
@can('estimate.destroy')
|
||||
<a href="javascript:void(0);"
|
||||
data-link="{{ route('estimate.destroy', $estimate->id) }}"
|
||||
data-id="{{ $estimate->id }}"
|
||||
class="link-danger fs-15 remove-item-btn"><i
|
||||
class="ri-delete-bin-line"></i></a>
|
||||
@endcan
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--end row-->
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
@ -0,0 +1,361 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row gy-3">
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-1">
|
||||
{{ html()->label('Date:')->class('form-label') }}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{ html()->text('date')->class('form-control flatpickr-date bg-light border-0')->placeholder('Date')->required() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="row align-items-center justify-content-end">
|
||||
<div class="col-2">
|
||||
{{ html()->label('Estimate#')->class('form-label') }}
|
||||
</div>
|
||||
|
||||
@php
|
||||
if ($editable) {
|
||||
$estimateNumber = $estimate->estimate_no;
|
||||
} else {
|
||||
$estimateNumber = generateEstimateNumber();
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="col-6">
|
||||
{{ html()->text('estimate_no', $estimateNumber)->class('form-control bg-light border-0') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
{{ html()->label('Customer')->class('form-label') }}
|
||||
{{ html()->select('customer_id', $customerList)->class('form-control select2')->placeholder('Customer Name')->required() }}
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
{{ html()->label('Validity')->class('form-label') }}
|
||||
{{ html()->text('validity')->class('form-control flatpickr-date bg-light border-0')->required() }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
{{ html()->label('Estimate for')->class('form-label') }}
|
||||
{{ html()->text('title')->class('form-control')->placeholder('Estimate for')->required() }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
{{ html()->label('Condition')->class('form-label') }}
|
||||
{{ html()->text('conditions')->class('form-control')->placeholder('Conditions')->required() }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12 mt-4">
|
||||
<fieldset class="rounded p-3 border-1 border-muted">
|
||||
<legend class="fs-6">Estimate Details</legend>
|
||||
|
||||
<div class="row gy-3 align-items-end">
|
||||
<div class="col-lg-3">
|
||||
{{ html()->label('Item')->class('form-label') }}
|
||||
{{ html()->select(null, $billingComponentList)->class('form-control select2 item')->placeholder('Billing Component') }}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-2">
|
||||
{{ html()->label('Price')->class('form-label') }}
|
||||
{{ html()->number(null)->class('form-control price')->placeholder('Price') }}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-2">
|
||||
{{ html()->label('Qty')->class('form-label') }}
|
||||
{{ html()->number(null)->class('form-control qty')->placeholder('Quantity') }}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3">
|
||||
{{ html()->label('Taxable')->class('form-label') }}
|
||||
{{ html()->select(null, ['1' => 'Yes', '0' => 'No'], 1)->class('form-control select2 taxable')->placeholder('Yes/No') }}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-2 text-end">
|
||||
<a href="javascript:void(0)"
|
||||
class="btn btn-soft-secondary fw-medium add-item-btn"><i
|
||||
class="ri-add-fill me-1 align-bottom"></i> Add Item</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered border-muted table-sm">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th scope="col">S.N</th>
|
||||
<th scope="col">Particular</th>
|
||||
<th scope="col">Qty</th>
|
||||
<th scope="col">Rate</th>
|
||||
<th scope="col" width=20%>Non-Taxable</th>
|
||||
<th scope="col" width=20%>Taxable</th>
|
||||
<th scope="col">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if ($editable)
|
||||
@foreach ($estimate->estimateDetails as $index => $item)
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{$index + 1}}
|
||||
</th>
|
||||
<td>${item->text}</td>
|
||||
<td>${price.value}</td>
|
||||
<td>${qty.value}</td>
|
||||
<td class="non-taxable-amount">${taxable.value == 1 ? '-' : qty.value * price.value}</td>
|
||||
<td class="taxable-amount">${taxable.value == 0 ? '-' : qty.value * price.value}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)" class="btn btn-sm btn-soft-danger remove-item-btn" data-index=${index}>
|
||||
<i class="ri-subtract-line align-middle"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@endif
|
||||
</tbody>
|
||||
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>
|
||||
{{ html()->text('totalNonTaxable')->class('form-control border-0')->id('non-taxable-total')->placeholder('0.00')->isReadOnly(true) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ html()->text('totalTaxable')->class('form-control border-0')->id('taxable-total')->placeholder('0.00')->isReadOnly(true) }}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end mb-2">
|
||||
<table class="table-borderless align-middle">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Tax(13%)</th>
|
||||
<td>
|
||||
{{ html()->text('tax')->class('form-control bg-light border-0')->id('tax')->placeholder('0.00')->isReadOnly(true) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Total Tax</th>
|
||||
<td>
|
||||
{{ html()->text('amountWithTax')->class('form-control bg-light border-0')->id('amount-with-tax')->placeholder('0.00')->isReadOnly(true) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-top border-top-dashed">
|
||||
<th scope="row">Grand Total</th>
|
||||
<td>
|
||||
{{ html()->text('grandTotal')->class('form-control bg-light border-0')->id('grand-total')->placeholder('0.00')->isReadOnly(true) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<x-form-buttons :editable="$editable" label="Add" href="{{ route('estimate.index') }}" />
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- end card body -->
|
||||
</div>
|
||||
<!-- end card -->
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@push('js')
|
||||
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
|
||||
let dataArray = [];
|
||||
|
||||
let index = 0;
|
||||
|
||||
$('.add-item-btn').click(function() {
|
||||
|
||||
const selectedElements = $('.item, .price, .qty, .taxable');
|
||||
|
||||
let entry = getSelectedValues(selectedElements);
|
||||
|
||||
console.log(entry);
|
||||
|
||||
if (entry) {
|
||||
updateTable(entry);
|
||||
calculate();
|
||||
$('.price, .qty').val('');
|
||||
}
|
||||
});
|
||||
|
||||
function getSelectedValues(selectedElements) {
|
||||
let isEmpty = false;
|
||||
let selectedValues = selectedElements.map((_, el) => {
|
||||
try {
|
||||
const $el = $(el);
|
||||
|
||||
if ($el.val() === '') {
|
||||
$el.focus();
|
||||
isEmpty = true;
|
||||
throw new Error('Empty value encountered');
|
||||
}
|
||||
|
||||
if ($el.is('select')) {
|
||||
return {
|
||||
text: $el.find('option:selected').text().trim(),
|
||||
value: $el.val(),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
value: $el.val(),
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("An error occurred:", error);
|
||||
return null;
|
||||
}
|
||||
}).get();
|
||||
|
||||
if (isEmpty) {
|
||||
|
||||
return null;
|
||||
|
||||
} else {
|
||||
|
||||
return selectedValues;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function updateTable(entry) {
|
||||
|
||||
const [item, price, qty, taxable] = entry;
|
||||
|
||||
try {
|
||||
|
||||
const data = {
|
||||
billing_component_id: item.value,
|
||||
price: price.value,
|
||||
qty: qty.value,
|
||||
taxable: taxable.value,
|
||||
tax_amount: taxable.value ? price.value * qty.value : 0,
|
||||
};
|
||||
|
||||
dataArray.push(data);
|
||||
|
||||
console.log(dataArray);
|
||||
|
||||
let tableBody = $('.table tbody');
|
||||
let html = `
|
||||
<tr>
|
||||
<th scope="row">
|
||||
${tableBody.children().length + 1}
|
||||
</th>
|
||||
<td>${item.text}</td>
|
||||
<td>${price.value}</td>
|
||||
<td>${qty.value}</td>
|
||||
<td class="non-taxable-amount">${taxable.value == 1 ? '-' : qty.value * price.value}</td>
|
||||
<td class="taxable-amount">${taxable.value == 0 ? '-' : qty.value * price.value}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)" class="btn btn-sm btn-soft-danger remove-item-btn" data-index=${index}>
|
||||
<i class="ri-subtract-line align-middle"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
tableBody.append(html);
|
||||
|
||||
index++;
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
function calculate() {
|
||||
let totalTaxable = 0;
|
||||
let totalNonTaxable = 0;
|
||||
let amountWithTax = 0;
|
||||
let grandTotal = 0;
|
||||
|
||||
$('.taxable-amount').each(function() {
|
||||
var value = parseFloat($(this).text());
|
||||
if (!isNaN(value)) {
|
||||
totalTaxable += value;
|
||||
}
|
||||
});
|
||||
|
||||
$('.non-taxable-amount').each(function() {
|
||||
var value = parseFloat($(this).text());
|
||||
if (!isNaN(value)) {
|
||||
totalNonTaxable += value;
|
||||
}
|
||||
});
|
||||
|
||||
taxAmount = (totalTaxable * 0.13).toFixed(2);
|
||||
amountWithTax = totalTaxable + parseFloat(taxAmount);
|
||||
grandTotal = (totalNonTaxable + parseFloat(amountWithTax)).toFixed(2);
|
||||
|
||||
$('#tax').val(taxAmount);
|
||||
$('#amount-with-tax').val(amountWithTax);
|
||||
$('#grand-total').val(grandTotal);
|
||||
$('#non-taxable-total').val(totalNonTaxable);
|
||||
$('#taxable-total').val(totalTaxable);
|
||||
}
|
||||
|
||||
$(".table").on('click', '.remove-item-btn', function() {
|
||||
removeItem(this);
|
||||
});
|
||||
|
||||
function removeItem(element) {
|
||||
$(element).closest('tr').remove();
|
||||
const dataIndex = $(element).data('index');
|
||||
console.log(dataIndex);
|
||||
dataArray.splice(dataIndex, 1);
|
||||
console.log(dataArray);
|
||||
calculate();
|
||||
};
|
||||
|
||||
$(document).on('submit', '#storeUpdateForm', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
let estimateDetails = JSON.stringify(dataArray);
|
||||
|
||||
let hiddenInput = $("<input>")
|
||||
.attr("type", "hidden")
|
||||
.attr("name", "estimateDetails")
|
||||
.val(estimateDetails);
|
||||
|
||||
$('#storeUpdateForm').append(hiddenInput);
|
||||
|
||||
this.submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
257
Modules/Estimate/resources/views/estimate/show.blade.php
Normal file
257
Modules/Estimate/resources/views/estimate/show.blade.php
Normal file
@ -0,0 +1,257 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="page-content">
|
||||
<div class="container-fluid">
|
||||
@include('layouts.partials.breadcrumb', ['title' => $title])
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-xxl-9">
|
||||
<div class="card" id="demo">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card-header border-bottom-dashed p-4 ribbon-box border shadow-none right mb-lg-0">
|
||||
<x-ribbon :status="$estimate->approval_status"/>
|
||||
<div class="d-flex mt-4">
|
||||
<div class="flex-grow-1">
|
||||
<img src="{{ asset('assets/images/logo-dark.png') }}"
|
||||
class="card-logo card-logo-dark" alt="logo dark" height="17">
|
||||
<img src="{{ asset('assets/images/logo-light.png') }}"
|
||||
class="card-logo card-logo-light" alt="logo light" height="17">
|
||||
<div class="mt-sm-5 mt-4">
|
||||
<h6 class="text-muted text-uppercase fw-semibold">Address</h6>
|
||||
<p class="text-muted mb-1" id="address-details">California, United States
|
||||
</p>
|
||||
<p class="text-muted mb-0" id="zip-code"><span>Zip-code:</span> 90201</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-shrink-0 mt-sm-0 mt-3">
|
||||
<h6><span class="text-muted fw-normal">Estimate No: </span><span
|
||||
id="legal-register-no">{{$estimate->estimate_no}}</span></h6>
|
||||
<h6><span class="text-muted fw-normal">Date: </span><span
|
||||
id="email">{{ $estimate->date->format('Y-m-d') }}</span></h6>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--end card-header-->
|
||||
</div><!--end col-->
|
||||
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card-body p-4 border-top border-top-dashed">
|
||||
<div class="row g-3">
|
||||
<div class="col-4">
|
||||
<fieldset class="border-2 text-muted">
|
||||
<legend>
|
||||
<h6 class="text-dark">To,</h6>
|
||||
</legend>
|
||||
<p class="fw-medium text-uppercase mb-2" id="billing-name">
|
||||
{{ $estimate->customer?->customer_name ?? 'N/A' }}</p>
|
||||
<p class="text-muted mb-1 text-uppercase" id="billing-address-line-1">
|
||||
{{ $estimate->customer?->address ?? 'N/A' }}</p>
|
||||
<p class="text-muted mb-1"><span>Phone: </span><span
|
||||
id="billing-phone-no">{{ $estimate->customer?->phone ?? 'N/A' }}</span>
|
||||
</p>
|
||||
|
||||
<div class="px-3 py-2 border border-1 border-dark m-2">
|
||||
<p class="text-dark text-bold mb-0 text-uppercase"><span>Client Pan:
|
||||
</span><span
|
||||
id="billing-tax-no">{{ $estimate->customer?->company_pan ?? 'N/A' }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
<!--end col-->
|
||||
|
||||
</div>
|
||||
<!--end row-->
|
||||
</div>
|
||||
<!--end card-body-->
|
||||
</div><!--end col-->
|
||||
<div class="col-lg-12">
|
||||
<div class="card-body p-4">
|
||||
<div class="table-reponsive">
|
||||
<table class="table table-bordered table-nowrap text-center align-middle mb-0">
|
||||
<thead>
|
||||
<tr class="table-active">
|
||||
<th scope="col" style="width: 50px;">#</th>
|
||||
<th scope="col" width=30%>Description</th>
|
||||
<th scope="col" width=20%>Narration</th>
|
||||
<th scope="col">Qty</th>
|
||||
<th scope="col">Rate</th>
|
||||
<th scope="col">Non Taxable</th>
|
||||
<th scope="col" class="text-center">Taxable</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody id="products-list">
|
||||
@if ($estimate->estimateDetails->isNotEmpty())
|
||||
@foreach ($estimate->estimateDetails as $index => $item)
|
||||
<tr>
|
||||
<th scope="row">{{ $index + 1 }}</th>
|
||||
<td class="text-center">
|
||||
<span class="fw-medium"></span>
|
||||
<p class="text-muted mb-0">
|
||||
{{ $item->billingComponent?->title ?? 'N/A' }}</p>
|
||||
</td>
|
||||
<td>{{ $item->billingComponent?->unit }}</td>
|
||||
<td>{{ $item->qty }}</td>
|
||||
<td class="text-center">{{ $item->billingComponent?->price }}
|
||||
</td>
|
||||
<td class="non-taxable-amount">{{ $item->billingComponent?->taxable == 0 ? $item->qty * $item->billingComponent?->price : '' }}
|
||||
</td>
|
||||
<td class="taxable-amount">{{ $item->billingComponent?->taxable == 1 ? $item->qty * $item->billingComponent?->price : '' }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@endif
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="4" class="text-start"> Amount in words:</td>
|
||||
<td class="text-end">Total (A):</td>
|
||||
<td id="non-taxable-total">0.00</td>
|
||||
<td class="taxable-total">0.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" rowspan="4" id="amount-in-words" class="text-capitalize"></td>
|
||||
<td colspan="2" class="text-end">Taxable Total</td>
|
||||
<td class="taxable-total">0.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="text-end">VAT (13%)</td>
|
||||
<td id="tax">0.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="text-end">Amount After Tax (B)</td>
|
||||
<td id="amount-with-tax">0.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="text-end">Grand Total (A+B)</td>
|
||||
<td class="grand-total">0.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
</table><!--end table-->
|
||||
</div>
|
||||
|
||||
{{-- <div class="mt-3">
|
||||
<h6 class="text-muted text-uppercase fw-semibold mb-3">Estimate Details:</h6>
|
||||
<p class="text-muted mb-1">Total Amount: <span class="fw-medium grand-total"
|
||||
id="card-number">0.00</span></p>
|
||||
<p class="text-muted">Amount in words: <span class="fw-medium" id="">$
|
||||
</span><span id="card-total-amount">One Lakh Five Thousand only /-</span></p>
|
||||
</div> --}}
|
||||
|
||||
<div class="hstack gap-2 justify-content-end d-print-none mt-4">
|
||||
<a href="javascript:window.print()" class="btn btn-info"><i
|
||||
class="ri-printer-line align-bottom me-1"></i> Print</a>
|
||||
|
||||
@if ($estimate->approval_status != 3)
|
||||
<a href="javascript:void(0);" class="btn btn-success" data-bs-toggle="modal"
|
||||
data-bs-target="#approveModal"><i
|
||||
class="ri-check-double-line align-bottom me-1"></i> Approve</a>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!--end card-body-->
|
||||
</div><!--end col-->
|
||||
</div><!--end row-->
|
||||
</div>
|
||||
<!--end card-->
|
||||
</div>
|
||||
<!--end col-->
|
||||
</div>
|
||||
<!--end row-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grids in modals -->
|
||||
<div class="modal fade" id="approveModal" tabindex="-1" aria-labelledby="approveModalLabel" aria-modal="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="approveModalLabel">Approve Form</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form action="{{ route('estimate.changeStatus') }}" class="needs-validation" method="POST"
|
||||
novalidate>
|
||||
@csrf
|
||||
<div class="row g-3">
|
||||
{{ html()->hidden('estimate_id', $item->id) }}
|
||||
<div class="col-12">
|
||||
<div>
|
||||
{{ html()->label('Status')->for('status')->class('form-label') }}
|
||||
{{ html()->select('approval_status', $statusList, $item->status)->id('status')->class('form-control select2') }}
|
||||
</div>
|
||||
</div><!--end col-->
|
||||
|
||||
<div class="col-12">
|
||||
<div>
|
||||
{{ html()->label('Remarks')->for('approval_remarks')->class('form-label') }}
|
||||
{{ html()->textarea('approval_remarks')->id('approval_remarks')->class('form-control')->attributes(['rows' => 3]) }}
|
||||
</div>
|
||||
</div><!--end col-->
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="hstack gap-2 justify-content-end">
|
||||
<button type="button" class="btn btn-sm btn-light"
|
||||
data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-sm btn-primary">Submit</button>
|
||||
</div>
|
||||
</div><!--end col-->
|
||||
|
||||
</div><!--end row-->
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('js')
|
||||
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
|
||||
<script class="text/javascript">
|
||||
|
||||
$(document).ready(function() {
|
||||
calculate();
|
||||
});
|
||||
|
||||
function calculate() {
|
||||
let totalTaxable = 0;
|
||||
let totalNonTaxable = 0;
|
||||
let amountWithTax = 0;
|
||||
let grandTotal = 0;
|
||||
|
||||
$('.taxable-amount').each(function() {
|
||||
var value = parseFloat($(this).text());
|
||||
if (!isNaN(value)) {
|
||||
totalTaxable += value;
|
||||
}
|
||||
});
|
||||
|
||||
$('.non-taxable-amount').each(function() {
|
||||
var value = parseFloat($(this).text());
|
||||
if (!isNaN(value)) {
|
||||
totalNonTaxable += value;
|
||||
}
|
||||
});
|
||||
|
||||
taxAmount = (totalTaxable * 0.13).toFixed(2);
|
||||
amountWithTax = totalTaxable + parseFloat(taxAmount);
|
||||
grandTotal = (totalNonTaxable + parseFloat(amountWithTax)).toFixed(2);
|
||||
const amountInWords = `${amountToWords(grandTotal)} Only /-`;
|
||||
|
||||
$('#tax').text(taxAmount);
|
||||
$('#amount-with-tax').text(amountWithTax);
|
||||
$('.grand-total').text(grandTotal);
|
||||
$('#non-taxable-total').text(totalNonTaxable);
|
||||
$('.taxable-total').text(totalTaxable);
|
||||
$('#amount-in-words').text(amountInWords);
|
||||
}
|
||||
</script>
|
||||
@endpush
|
@ -0,0 +1,15 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="page-content">
|
||||
<div class="container-fluid">
|
||||
@include('layouts.partials.breadcrumb', ['title' => $title])
|
||||
|
||||
{{ html()->form('POST')->route('estimate.store')->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
|
||||
@include('estimate::estimate.partials.action')
|
||||
{{ html()->form()->close() }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -0,0 +1,27 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="page-content">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- start page title -->
|
||||
|
||||
@include('layouts.partials.breadcrumb', ['title' => $title])
|
||||
|
||||
<!-- end page title -->
|
||||
|
||||
{{ html()->modelForm($estimate, 'PUT')->route('estimate.update', $estimate->id)->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
|
||||
|
||||
@include('estimate::estimate.partials.action')
|
||||
|
||||
{{ html()->closeModelForm() }}
|
||||
<!--end row-->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- container-fluid -->
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
@ -0,0 +1,78 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="page-content">
|
||||
<div class="container-fluid">
|
||||
@include('layouts.partials.breadcrumb', ['title' => $title])
|
||||
|
||||
|
||||
<div class="mb-2 text-end">
|
||||
@can('estimateDetail.create')
|
||||
<a href="{{ route('estimateDetail.create') }}" class="btn btn-success btn-md waves-effect waves-light"><i
|
||||
class="ri-add-fill me-1 align-bottom"></i> Add</a>
|
||||
@endcan
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="buttons-datatables" class="display table-sm table-bordered table"
|
||||
style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>S.N</th>
|
||||
<th>Estimate</th>
|
||||
<th>Billing Component</th>
|
||||
<th>Qty</th>
|
||||
<th>Price</th>
|
||||
<th>Tax Amount</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($estimateDetails as $key => $estimateDetail)
|
||||
<tr>
|
||||
<td>{{ $key + 1 }}</td>
|
||||
<td>{{ $estimateDetail->estimate_id }}</td>
|
||||
<td>{{ $estimateDetail->billing_component_id }}</td>
|
||||
<td>{{ $estimateDetail->qty }}</td>
|
||||
<td>{{ $estimateDetail->price }}</td>
|
||||
<td>{{ $estimateDetail->tax_amount }}</td>
|
||||
<td>
|
||||
<div class="hstack flex-wrap gap-3">
|
||||
@can('estimateDetail.show')
|
||||
<a href="{{ route('estimateDetail.show', $estimateDetail->id) }}"
|
||||
class="link-info fs-15">
|
||||
<i class="ri-eye-line"></i>
|
||||
</a>
|
||||
@endcan
|
||||
@can('estimateDetail.edit')
|
||||
<a href="{{ route('estimateDetail.edit', $estimateDetail->id) }}"
|
||||
class="link-success fs-15 edit-item-btn"><i
|
||||
class="ri-edit-2-line"></i></a>
|
||||
@endcan
|
||||
@can('estimateDetail.destroy')
|
||||
<a href="javascript:void(0);"
|
||||
data-link="{{ route('estimateDetail.destroy', $estimateDetail->id) }}"
|
||||
data-id="{{ $estimateDetail->id }}"
|
||||
class="link-danger fs-15 remove-item-btn"><i
|
||||
class="ri-delete-bin-line"></i></a>
|
||||
@endcan
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--end row-->
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
@ -0,0 +1,79 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div class="card">
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row gy-3">
|
||||
|
||||
<div class="col-md-4">
|
||||
{{ html()->label('Customer Name')->class('form-label') }}
|
||||
{{ html()->select('customer_id', $customerList)->class('form-control select2')->placeholder('Customer Name')->required() }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
{{ html()->label('Title')->class('form-label') }}
|
||||
{{ html()->text('title')->class('form-control')->placeholder('Estimate Title')->required() }}
|
||||
{{ html()->div('Please enter estimate title')->class('invalid-feedback') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
{{ html()->label('Date')->class('form-label') }}
|
||||
{{ html()->text('date')->class('form-control flatpickr-date')->placeholder('Estimate Date')->required() }}
|
||||
{{ html()->div('Please enter estimate date')->class('invalid-feedback') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
{{ html()->label('Validity')->class('form-label') }}
|
||||
{{ html()->text('validity')->class('form-control flatpickr-date')->placeholder('Validity Date')->required() }}
|
||||
{{ html()->div('Please enter validity date')->class('invalid-feedback') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
{{ html()->label('Approver Name')->class('form-label') }}
|
||||
{{ html()->select('approval_by', $customerList)->class('form-control select2')->placeholder('Approver Name') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
{{ html()->label('Approval Date')->class('form-label') }}
|
||||
{{ html()->text('approval_on')->class('form-control flatpickr-date')->placeholder('Approval Date') }}
|
||||
{{ html()->div('Please enter approval date')->class('invalid-feedback') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
{{ html()->label('Conditions')->class('form-label') }}
|
||||
{{ html()->textarea('conditions')->class('form-control')->placeholder('Conditions')->attributes(['rows' => 3]) }}
|
||||
</div>
|
||||
|
||||
<x-form-buttons :editable="$editable" label="Add" href="{{ route('estimate.index') }}" />
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- end card body -->
|
||||
</div>
|
||||
<!-- end card -->
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0 fs-6">Approval Status</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
{{ html()->label('Status')->class('form-label') }}
|
||||
{{ html()->select('approval_status', $approvalStatusList)->class('form-control select2')->placeholder('Approval Status')->required() }}
|
||||
{{ html()->div('Please enter approval status')->class('invalid-feedback') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('js')
|
||||
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
|
||||
@endpush
|
@ -0,0 +1,45 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="page-content">
|
||||
<div class="container-fluid">
|
||||
@include('layouts.partials.breadcrumb', ['title' => $title])
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card card-body p-4">
|
||||
<div>
|
||||
<div class="table-responsive">
|
||||
<table class="table-borderless mb-0 table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><span class="fw-medium">Estimate Name</span></th>
|
||||
<td>{{ $estimate->estimate_name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="fw-medium">Email</span></th>
|
||||
<td>{{ $estimate->email }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="fw-medium">Phone</span></th>
|
||||
<td>{{ $estimate->contact }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 text-end">
|
||||
<a href="{{ route('estimate.index') }}" class="btn btn-secondary w-sm">Back</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('js')
|
||||
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
|
||||
@endpush
|
7
Modules/Estimate/resources/views/index.blade.php
Normal file
7
Modules/Estimate/resources/views/index.blade.php
Normal file
@ -0,0 +1,7 @@
|
||||
@extends('estimate::layouts.master')
|
||||
|
||||
@section('content')
|
||||
<h1>Hello World</h1>
|
||||
|
||||
<p>Module: {!! config('estimate.name') !!}</p>
|
||||
@endsection
|
29
Modules/Estimate/resources/views/layouts/master.blade.php
Normal file
29
Modules/Estimate/resources/views/layouts/master.blade.php
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
<title>Estimate Module - {{ config('app.name', 'Laravel') }}</title>
|
||||
|
||||
<meta name="description" content="{{ $description ?? '' }}">
|
||||
<meta name="keywords" content="{{ $keywords ?? '' }}">
|
||||
<meta name="author" content="{{ $author ?? '' }}">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
||||
|
||||
{{-- Vite CSS --}}
|
||||
{{-- {{ module_vite('build-estimate', 'resources/assets/sass/app.scss') }} --}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@yield('content')
|
||||
|
||||
{{-- Vite JS --}}
|
||||
{{-- {{ module_vite('build-estimate', 'resources/assets/js/app.js') }} --}}
|
||||
</body>
|
0
Modules/Estimate/routes/.gitkeep
Normal file
0
Modules/Estimate/routes/.gitkeep
Normal file
19
Modules/Estimate/routes/api.php
Normal file
19
Modules/Estimate/routes/api.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Estimate\Http\Controllers\EstimateController;
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------------
|
||||
* 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:sanctum'])->prefix('v1')->group(function () {
|
||||
Route::apiResource('estimate', EstimateController::class)->names('estimate');
|
||||
});
|
21
Modules/Estimate/routes/web.php
Normal file
21
Modules/Estimate/routes/web.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Estimate\Http\Controllers\EstimateController;
|
||||
use Modules\Estimate\Http\Controllers\EstimateDetailController;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 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([], function () {
|
||||
Route::post('change-status', [EstimateController::class,'changeStatus'])->name('estimate.changeStatus');
|
||||
Route::resource('estimate', EstimateController::class)->names('estimate');
|
||||
});
|
26
Modules/Estimate/vite.config.js
Normal file
26
Modules/Estimate/vite.config.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import laravel from 'laravel-vite-plugin';
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: '../../public/build-estimate',
|
||||
emptyOutDir: true,
|
||||
manifest: true,
|
||||
},
|
||||
plugins: [
|
||||
laravel({
|
||||
publicDirectory: '../../public',
|
||||
buildDirectory: 'build-estimate',
|
||||
input: [
|
||||
__dirname + '/resources/assets/sass/app.scss',
|
||||
__dirname + '/resources/assets/js/app.js'
|
||||
],
|
||||
refresh: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
//export const paths = [
|
||||
// 'Modules/Estimate/resources/assets/sass/app.scss',
|
||||
// 'Modules/Estimate/resources/assets/js/app.js',
|
||||
//];
|
Reference in New Issue
Block a user