Added: Quotation Module
This commit is contained in:
parent
e6ce2f5928
commit
502e599eb3
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'name' => 'Quotation'
|
||||
];
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\DataTables;
|
||||
|
||||
use Modules\Quotation\Entities\Quotation;
|
||||
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 QuotationsDataTable extends DataTable
|
||||
{
|
||||
|
||||
public function dataTable($query) {
|
||||
return datatables()
|
||||
->eloquent($query)
|
||||
->addColumn('total_amount', function ($data) {
|
||||
return format_currency($data->total_amount);
|
||||
})
|
||||
->addColumn('status', function ($data) {
|
||||
return view('quotation::partials.status', compact('data'));
|
||||
})
|
||||
->addColumn('action', function ($data) {
|
||||
return view('quotation::partials.actions', compact('data'));
|
||||
});
|
||||
}
|
||||
|
||||
public function query(Quotation $model) {
|
||||
return $model->newQuery();
|
||||
}
|
||||
|
||||
public function html() {
|
||||
return $this->builder()
|
||||
->setTableId('sales-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(6)
|
||||
->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')
|
||||
);
|
||||
}
|
||||
|
||||
protected function getColumns() {
|
||||
return [
|
||||
Column::make('date')
|
||||
->className('text-center align-middle'),
|
||||
|
||||
Column::make('reference')
|
||||
->className('text-center align-middle'),
|
||||
|
||||
Column::make('customer_name')
|
||||
->title('Customer')
|
||||
->className('text-center align-middle'),
|
||||
|
||||
Column::computed('status')
|
||||
->className('text-center align-middle'),
|
||||
|
||||
Column::computed('total_amount')
|
||||
->className('text-center align-middle'),
|
||||
|
||||
Column::computed('action')
|
||||
->exportable(false)
|
||||
->printable(false)
|
||||
->className('text-center align-middle'),
|
||||
|
||||
Column::make('created_at')
|
||||
->visible(false)
|
||||
];
|
||||
}
|
||||
|
||||
protected function filename() {
|
||||
return 'Quotations_' . date('YmdHis');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateQuotationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('quotations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->date('date');
|
||||
$table->string('reference');
|
||||
$table->unsignedBigInteger('customer_id')->nullable();
|
||||
$table->string('customer_name');
|
||||
$table->integer('tax_percentage')->default(0);
|
||||
$table->integer('tax_amount')->default(0);
|
||||
$table->integer('discount_percentage')->default(0);
|
||||
$table->integer('discount_amount')->default(0);
|
||||
$table->integer('shipping_amount')->default(0);
|
||||
$table->integer('total_amount');
|
||||
$table->string('status');
|
||||
$table->text('note')->nullable();
|
||||
$table->foreign('customer_id')->references('id')->on('customers')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('quotations');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateQuotationDetailsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('quotation_details', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('quotation_id');
|
||||
$table->unsignedBigInteger('product_id')->nullable();
|
||||
$table->string('product_name');
|
||||
$table->string('product_code');
|
||||
$table->integer('quantity');
|
||||
$table->integer('price');
|
||||
$table->integer('unit_price');
|
||||
$table->integer('sub_total');
|
||||
$table->integer('product_discount_amount');
|
||||
$table->string('product_discount_type')->default('fixed');
|
||||
$table->integer('product_tax_amount');
|
||||
$table->foreign('quotation_id')->references('id')
|
||||
->on('quotations')->cascadeOnDelete();
|
||||
$table->foreign('product_id')->references('id')
|
||||
->on('products')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('quotation_details');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class QuotationDatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Model::unguard();
|
||||
|
||||
// $this->call("OthersTableSeeder");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Emails;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class QuotationMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $quotation;
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($quotation)
|
||||
{
|
||||
$this->quotation = $quotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->subject('Quotation - ' . settings()->company_name)
|
||||
->view('quotation::emails.quotation', [
|
||||
'settings' => settings(),
|
||||
'customer' => $this->quotation->customer
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Entities;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Modules\People\Entities\Customer;
|
||||
|
||||
class Quotation extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
public function quotationDetails() {
|
||||
return $this->hasMany(QuotationDetails::class, 'quotation_id', 'id');
|
||||
}
|
||||
|
||||
public function customer() {
|
||||
return $this->belongsTo(Customer::class, 'customer_id', 'id');
|
||||
}
|
||||
|
||||
public static function boot() {
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($model) {
|
||||
$number = Quotation::max('id') + 1;
|
||||
$model->reference = make_reference_id('QT', $number);
|
||||
});
|
||||
}
|
||||
|
||||
public function getDateAttribute($value) {
|
||||
return Carbon::parse($value)->format('d M, Y');
|
||||
}
|
||||
|
||||
public function getShippingAmountAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
|
||||
public function getPaidAmountAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
|
||||
public function getTotalAmountAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
|
||||
public function getDueAmountAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
|
||||
public function getTaxAmountAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
|
||||
public function getDiscountAmountAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Entities;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Modules\Product\Entities\Product;
|
||||
|
||||
class QuotationDetails extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $with = ['product'];
|
||||
|
||||
public function product() {
|
||||
return $this->belongsTo(Product::class, 'product_id', 'id');
|
||||
}
|
||||
|
||||
public function quotation() {
|
||||
return $this->belongsTo(Quotation::class, 'quotation_id', 'id');
|
||||
}
|
||||
|
||||
public function getPriceAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
|
||||
public function getUnitPriceAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
|
||||
public function getSubTotalAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
|
||||
public function getProductDiscountAmountAttribute($value) {
|
||||
return $value / 100;
|
||||
}
|
||||
|
||||
public function getProductTaxAmountAttribute($value) {
|
||||
return $value / 100;
|
||||
}}
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Http\Controllers;
|
||||
|
||||
use Gloudemans\Shoppingcart\Facades\Cart;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Modules\People\Entities\Customer;
|
||||
use Modules\Product\Entities\Product;
|
||||
use Modules\Quotation\DataTables\QuotationsDataTable;
|
||||
use Modules\Quotation\Entities\Quotation;
|
||||
use Modules\Quotation\Entities\QuotationDetails;
|
||||
use Modules\Quotation\Http\Requests\StoreQuotationRequest;
|
||||
use Modules\Quotation\Http\Requests\UpdateQuotationRequest;
|
||||
|
||||
class QuotationController extends Controller
|
||||
{
|
||||
|
||||
public function index(QuotationsDataTable $dataTable) {
|
||||
abort_if(Gate::denies('access_quotations'), 403);
|
||||
|
||||
return $dataTable->render('quotation::index');
|
||||
}
|
||||
|
||||
|
||||
public function create() {
|
||||
abort_if(Gate::denies('create_quotations'), 403);
|
||||
|
||||
Cart::instance('quotation')->destroy();
|
||||
|
||||
return view('quotation::create');
|
||||
}
|
||||
|
||||
|
||||
public function store(StoreQuotationRequest $request) {
|
||||
DB::transaction(function () use ($request) {
|
||||
$quotation = Quotation::create([
|
||||
'date' => $request->date,
|
||||
'customer_id' => $request->customer_id,
|
||||
'customer_name' => Customer::findOrFail($request->customer_id)->customer_name,
|
||||
'tax_percentage' => $request->tax_percentage,
|
||||
'discount_percentage' => $request->discount_percentage,
|
||||
'shipping_amount' => $request->shipping_amount * 100,
|
||||
'total_amount' => $request->total_amount * 100,
|
||||
'status' => $request->status,
|
||||
'note' => $request->note,
|
||||
'tax_amount' => Cart::instance('quotation')->tax() * 100,
|
||||
'discount_amount' => Cart::instance('quotation')->discount() * 100,
|
||||
]);
|
||||
|
||||
foreach (Cart::instance('quotation')->content() as $cart_item) {
|
||||
QuotationDetails::create([
|
||||
'quotation_id' => $quotation->id,
|
||||
'product_id' => $cart_item->id,
|
||||
'product_name' => $cart_item->name,
|
||||
'product_code' => $cart_item->options->code,
|
||||
'quantity' => $cart_item->qty,
|
||||
'price' => $cart_item->price * 100,
|
||||
'unit_price' => $cart_item->options->unit_price * 100,
|
||||
'sub_total' => $cart_item->options->sub_total * 100,
|
||||
'product_discount_amount' => $cart_item->options->product_discount * 100,
|
||||
'product_discount_type' => $cart_item->options->product_discount_type,
|
||||
'product_tax_amount' => $cart_item->options->product_tax * 100,
|
||||
]);
|
||||
}
|
||||
|
||||
Cart::instance('quotation')->destroy();
|
||||
});
|
||||
|
||||
toast('Quotation Created!', 'success');
|
||||
|
||||
return redirect()->route('quotations.index');
|
||||
}
|
||||
|
||||
|
||||
public function show(Quotation $quotation) {
|
||||
abort_if(Gate::denies('show_quotations'), 403);
|
||||
|
||||
$customer = Customer::findOrFail($quotation->customer_id);
|
||||
|
||||
return view('quotation::show', compact('quotation', 'customer'));
|
||||
}
|
||||
|
||||
|
||||
public function edit(Quotation $quotation) {
|
||||
abort_if(Gate::denies('edit_quotations'), 403);
|
||||
|
||||
$quotation_details = $quotation->quotationDetails;
|
||||
|
||||
Cart::instance('quotation')->destroy();
|
||||
|
||||
$cart = Cart::instance('quotation');
|
||||
|
||||
foreach ($quotation_details as $quotation_detail) {
|
||||
$cart->add([
|
||||
'id' => $quotation_detail->product_id,
|
||||
'name' => $quotation_detail->product_name,
|
||||
'qty' => $quotation_detail->quantity,
|
||||
'price' => $quotation_detail->price,
|
||||
'weight' => 1,
|
||||
'options' => [
|
||||
'product_discount' => $quotation_detail->product_discount_amount,
|
||||
'product_discount_type' => $quotation_detail->product_discount_type,
|
||||
'sub_total' => $quotation_detail->sub_total,
|
||||
'code' => $quotation_detail->product_code,
|
||||
'stock' => Product::findOrFail($quotation_detail->product_id)->product_quantity,
|
||||
'product_tax' => $quotation_detail->product_tax_amount,
|
||||
'unit_price' => $quotation_detail->unit_price
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return view('quotation::edit', compact('quotation'));
|
||||
}
|
||||
|
||||
|
||||
public function update(UpdateQuotationRequest $request, Quotation $quotation) {
|
||||
DB::transaction(function () use ($request, $quotation) {
|
||||
foreach ($quotation->quotationDetails as $quotation_detail) {
|
||||
$quotation_detail->delete();
|
||||
}
|
||||
|
||||
$quotation->update([
|
||||
'date' => $request->date,
|
||||
'reference' => $request->reference,
|
||||
'customer_id' => $request->customer_id,
|
||||
'customer_name' => Customer::findOrFail($request->customer_id)->customer_name,
|
||||
'tax_percentage' => $request->tax_percentage,
|
||||
'discount_percentage' => $request->discount_percentage,
|
||||
'shipping_amount' => $request->shipping_amount * 100,
|
||||
'total_amount' => $request->total_amount * 100,
|
||||
'status' => $request->status,
|
||||
'note' => $request->note,
|
||||
'tax_amount' => Cart::instance('quotation')->tax() * 100,
|
||||
'discount_amount' => Cart::instance('quotation')->discount() * 100,
|
||||
]);
|
||||
|
||||
foreach (Cart::instance('quotation')->content() as $cart_item) {
|
||||
QuotationDetails::create([
|
||||
'quotation_id' => $quotation->id,
|
||||
'product_id' => $cart_item->id,
|
||||
'product_name' => $cart_item->name,
|
||||
'product_code' => $cart_item->options->code,
|
||||
'quantity' => $cart_item->qty,
|
||||
'price' => $cart_item->price * 100,
|
||||
'unit_price' => $cart_item->options->unit_price * 100,
|
||||
'sub_total' => $cart_item->options->sub_total * 100,
|
||||
'product_discount_amount' => $cart_item->options->product_discount * 100,
|
||||
'product_discount_type' => $cart_item->options->product_discount_type,
|
||||
'product_tax_amount' => $cart_item->options->product_tax * 100,
|
||||
]);
|
||||
}
|
||||
|
||||
Cart::instance('quotation')->destroy();
|
||||
});
|
||||
|
||||
toast('Quotation Updated!', 'info');
|
||||
|
||||
return redirect()->route('quotations.index');
|
||||
}
|
||||
|
||||
|
||||
public function destroy(Quotation $quotation) {
|
||||
abort_if(Gate::denies('delete_quotations'), 403);
|
||||
|
||||
$quotation->delete();
|
||||
|
||||
toast('Quotation Deleted!', 'warning');
|
||||
|
||||
return redirect()->route('quotations.index');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Http\Controllers;
|
||||
|
||||
use Gloudemans\Shoppingcart\Facades\Cart;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Modules\Product\Entities\Product;
|
||||
use Modules\Quotation\Entities\Quotation;
|
||||
use Modules\Quotation\Http\Requests\StoreQuotationSaleRequest;
|
||||
|
||||
class QuotationSalesController extends Controller
|
||||
{
|
||||
|
||||
public function __invoke(Quotation $quotation) {
|
||||
abort_if(Gate::denies('create_quotation_sales'), 403);
|
||||
|
||||
$quotation_details = $quotation->quotationDetails;
|
||||
|
||||
Cart::instance('sale')->destroy();
|
||||
|
||||
$cart = Cart::instance('sale');
|
||||
|
||||
foreach ($quotation_details as $quotation_detail) {
|
||||
$cart->add([
|
||||
'id' => $quotation_detail->product_id,
|
||||
'name' => $quotation_detail->product_name,
|
||||
'qty' => $quotation_detail->quantity,
|
||||
'price' => $quotation_detail->price,
|
||||
'weight' => 1,
|
||||
'options' => [
|
||||
'product_discount' => $quotation_detail->product_discount_amount,
|
||||
'product_discount_type' => $quotation_detail->product_discount_type,
|
||||
'sub_total' => $quotation_detail->sub_total,
|
||||
'code' => $quotation_detail->product_code,
|
||||
'stock' => Product::findOrFail($quotation_detail->product_id)->product_quantity,
|
||||
'product_tax' => $quotation_detail->product_tax_amount,
|
||||
'unit_price' => $quotation_detail->unit_price
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return view('quotation::quotation-sales.create', [
|
||||
'quotation_id' => $quotation->id,
|
||||
'sale' => $quotation
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Http\Controllers;
|
||||
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Modules\Quotation\Emails\QuotationMail;
|
||||
use Modules\Quotation\Entities\Quotation;
|
||||
|
||||
class SendQuotationEmailController extends Controller
|
||||
{
|
||||
public function __invoke(Quotation $quotation) {
|
||||
try {
|
||||
Mail::to($quotation->customer->customer_email)->send(new QuotationMail($quotation));
|
||||
|
||||
$quotation->update([
|
||||
'status' => 'Sent'
|
||||
]);
|
||||
|
||||
toast('Sent On "' . $quotation->customer->customer_email . '"!', 'success');
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
Log::error($exception);
|
||||
toast('Something Went Wrong!', 'error');
|
||||
}
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreQuotationRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'customer_id' => 'required|numeric',
|
||||
'reference' => 'required|string|max:255',
|
||||
'tax_percentage' => 'required|integer|min:0|max:100',
|
||||
'discount_percentage' => 'required|integer|min:0|max:100',
|
||||
'shipping_amount' => 'required|numeric',
|
||||
'total_amount' => 'required|numeric',
|
||||
'status' => 'required|string|max:255',
|
||||
'note' => 'nullable|string|max:1000'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return Gate::allows('create_quotations');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class UpdateQuotationRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'customer_id' => 'required|numeric',
|
||||
'reference' => 'required|string|max:255',
|
||||
'tax_percentage' => 'required|integer|min:0|max:100',
|
||||
'discount_percentage' => 'required|integer|min:0|max:100',
|
||||
'shipping_amount' => 'required|numeric',
|
||||
'total_amount' => 'required|numeric',
|
||||
'status' => 'required|string|max:255',
|
||||
'note' => 'nullable|string|max:1000'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return Gate::allows('edit_quotations');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Database\Eloquent\Factory;
|
||||
|
||||
class QuotationServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* @var string $moduleName
|
||||
*/
|
||||
protected $moduleName = 'Quotation';
|
||||
|
||||
/**
|
||||
* @var string $moduleNameLower
|
||||
*/
|
||||
protected $moduleNameLower = 'quotation';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Quotation\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\Quotation\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('Quotation', '/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('Quotation', '/Routes/api.php'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Create Quotation')
|
||||
|
||||
@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('quotations.index') }}">Quotations</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 id="quotation-form" action="{{ route('quotations.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col-lg-4">
|
||||
<div class="form-group">
|
||||
<label for="reference">Reference <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="reference" required readonly value="QT">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="from-group">
|
||||
<div class="form-group">
|
||||
<label for="customer_id">Customer <span class="text-danger">*</span></label>
|
||||
<select class="form-control" name="customer_id" id="customer_id" required>
|
||||
@foreach(\Modules\People\Entities\Customer::all() as $customer)
|
||||
<option value="{{ $customer->id }}">{{ $customer->customer_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<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="{{ now()->format('Y-m-d') }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<livewire:product-cart :cartInstance="'quotation'"/>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col-lg-4">
|
||||
<div class="form-group">
|
||||
<label for="status">Status <span class="text-danger">*</span></label>
|
||||
<select class="form-control" name="status" id="status" required>
|
||||
<option value="Pending">Pending</option>
|
||||
<option value="Sent">Sent</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 Quotation <i class="bi bi-check"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('page_scripts')
|
||||
|
||||
@endpush
|
|
@ -0,0 +1,92 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Edit Quotation')
|
||||
|
||||
@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('quotations.index') }}">Quotations</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 id="quotation-form" action="{{ route('quotations.update', $quotation) }}" method="POST">
|
||||
@csrf
|
||||
@method('patch')
|
||||
<div class="form-row">
|
||||
<div class="col-lg-4">
|
||||
<div class="form-group">
|
||||
<label for="reference">Reference <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="reference" required value="{{ $quotation->reference }}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="from-group">
|
||||
<div class="form-group">
|
||||
<label for="customer_id">Customer <span class="text-danger">*</span></label>
|
||||
<select class="form-control" name="customer_id" id="customer_id" required>
|
||||
@foreach(\Modules\People\Entities\Customer::all() as $customer)
|
||||
<option {{ $quotation->customer_id == $customer->id ? 'selected' : '' }} value="{{ $customer->id }}">{{ $customer->customer_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<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="{{ $quotation->getAttributes()['date'] }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<livewire:product-cart :cartInstance="'quotation'" :data="$quotation"/>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col-lg-4">
|
||||
<div class="form-group">
|
||||
<label for="status">Status <span class="text-danger">*</span></label>
|
||||
<select class="form-control" name="status" id="status" required>
|
||||
<option {{ $quotation->status == 'Pending' ? 'selected' : '' }} value="Pending">Pending</option>
|
||||
<option {{ $quotation->status == 'Sent' ? 'selected' : '' }} value="Sent">Sent</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="note">Note (If Needed)</label>
|
||||
<textarea name="note" id="note" rows="5" class="form-control">{{ $quotation->note }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Update Quotation <i class="bi bi-check"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('page_scripts')
|
||||
|
||||
@endpush
|
|
@ -0,0 +1,120 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Quotation Details</title>
|
||||
<link rel="stylesheet" href="{{ asset('b3/bootstrap.min.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid" style="margin: 20px 0;">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div style="text-align: center;margin-bottom: 25px;">
|
||||
<img width="180" src="{{ asset('images/logo-dark.png') }}" alt="Logo">
|
||||
<h4 style="margin-bottom: 20px;">
|
||||
<span>Reference::</span> <strong>{{ $quotation->reference }}</strong>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 mb-3 mb-md-0">
|
||||
<h4 class="mb-2" style="border-bottom: 1px solid #dddddd;padding-bottom: 10px;">Company Info:</h4>
|
||||
<div><strong>{{ settings()->company_name }}</strong></div>
|
||||
<div>{{ settings()->company_address }}</div>
|
||||
<div>Email: {{ settings()->company_email }}</div>
|
||||
<div>Phone: {{ settings()->company_phone }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3 mb-md-0">
|
||||
<h4 class="mb-2" style="border-bottom: 1px solid #dddddd;padding-bottom: 10px;">Customer Info:</h4>
|
||||
<div><strong>{{ $customer->customer_name }}</strong></div>
|
||||
<div>{{ $customer->address }}</div>
|
||||
<div>Email: {{ $customer->customer_email }}</div>
|
||||
<div>Phone: {{ $customer->customer_phone }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive" style="margin-top: 30px;">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="align-middle">Product</th>
|
||||
<th class="align-middle">Net Unit Price</th>
|
||||
<th class="align-middle">Quantity</th>
|
||||
<th class="align-middle">Discount</th>
|
||||
<th class="align-middle">Tax</th>
|
||||
<th class="align-middle">Sub Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($quotation->quotationDetails as $item)
|
||||
<tr>
|
||||
<td class="align-middle">
|
||||
{{ $item->product_name }} <br>
|
||||
<span class="badge badge-success">
|
||||
{{ $item->product_code }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="align-middle">{{ format_currency($item->unit_price) }}</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ $item->quantity }}
|
||||
</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ format_currency($item->product_discount_amount) }}
|
||||
</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ format_currency($item->product_tax_amount) }}
|
||||
</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ format_currency($item->sub_total) }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-md-offset-8">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="left"><strong>Discount ({{ $quotation->discount_percentage }}%)</strong></td>
|
||||
<td class="right">{{ format_currency($quotation->discount_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Tax ({{ $quotation->tax_percentage }}%)</strong></td>
|
||||
<td class="right">{{ format_currency($quotation->tax_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Shipping)</strong></td>
|
||||
<td class="right">{{ format_currency($quotation->shipping_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Grand Total</strong></td>
|
||||
<td class="right"><strong>{{ format_currency($quotation->total_amount) }}</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 25px;">
|
||||
<div class="col-md-12">
|
||||
<p style="font-style: italic;text-align: center">{{ settings()->company_name }} © {{ date('Y') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,40 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Quotations')
|
||||
|
||||
@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">Quotations</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('quotations.create') }}" class="btn btn-primary">
|
||||
Add Quotation <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
|
|
@ -0,0 +1,40 @@
|
|||
<div class="btn-group dropleft">
|
||||
<button type="button" class="btn btn-ghost-primary dropdown rounded" data-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
@can('create_quotation_sales')
|
||||
<a href="{{ route('quotation-sales.create', $data) }}" class="dropdown-item">
|
||||
<i class="bi bi-check2-circle mr-2 text-success" style="line-height: 1;"></i> Make Sale
|
||||
</a>
|
||||
@endcan
|
||||
@can('send_quotation_mails')
|
||||
<a href="{{ route('quotation.email', $data) }}" class="dropdown-item">
|
||||
<i class="bi bi-cursor mr-2 text-warning" style="line-height: 1;"></i> Send On Email
|
||||
</a>
|
||||
@endcan
|
||||
@can('edit_quotations')
|
||||
<a href="{{ route('quotations.edit', $data->id) }}" class="dropdown-item">
|
||||
<i class="bi bi-pencil mr-2 text-primary" style="line-height: 1;"></i> Edit
|
||||
</a>
|
||||
@endcan
|
||||
@can('show_quotations')
|
||||
<a href="{{ route('quotations.show', $data->id) }}" class="dropdown-item">
|
||||
<i class="bi bi-eye mr-2 text-info" style="line-height: 1;"></i> Details
|
||||
</a>
|
||||
@endcan
|
||||
@can('delete_quotations')
|
||||
<button id="delete" class="dropdown-item" 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 mr-2 text-danger" style="line-height: 1;"></i> Delete
|
||||
<form id="destroy{{ $data->id }}" class="d-none" action="{{ route('quotations.destroy', $data->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('delete')
|
||||
</form>
|
||||
</button>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
@if ($data->status == 'Pending')
|
||||
<span class="badge badge-info">
|
||||
{{ $data->status }}
|
||||
</span>
|
||||
@else
|
||||
<span class="badge badge-success">
|
||||
{{ $data->status }}
|
||||
</span>
|
||||
@endif
|
|
@ -0,0 +1,133 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Quotation Details</title>
|
||||
<link rel="stylesheet" href="{{ public_path('b3/bootstrap.min.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div style="text-align: center;margin-bottom: 25px;">
|
||||
<img width="180" src="{{ public_path('images/logo-dark.png') }}" alt="Logo">
|
||||
<h4 style="margin-bottom: 20px;">
|
||||
<span>Reference::</span> <strong>{{ $quotation->reference }}</strong>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row mb-4">
|
||||
<div class="col-xs-4 mb-3 mb-md-0">
|
||||
<h4 class="mb-2" style="border-bottom: 1px solid #dddddd;padding-bottom: 10px;">Company Info:</h4>
|
||||
<div><strong>{{ settings()->company_name }}</strong></div>
|
||||
<div>{{ settings()->company_address }}</div>
|
||||
<div>Email: {{ settings()->company_email }}</div>
|
||||
<div>Phone: {{ settings()->company_phone }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-4 mb-3 mb-md-0">
|
||||
<h4 class="mb-2" style="border-bottom: 1px solid #dddddd;padding-bottom: 10px;">Customer Info:</h4>
|
||||
<div><strong>{{ $customer->customer_name }}</strong></div>
|
||||
<div>{{ $customer->address }}</div>
|
||||
<div>Email: {{ $customer->customer_email }}</div>
|
||||
<div>Phone: {{ $customer->customer_phone }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-4 mb-3 mb-md-0">
|
||||
<h4 class="mb-2" style="border-bottom: 1px solid #dddddd;padding-bottom: 10px;">Invoice Info:</h4>
|
||||
<div>Invoice: <strong>INV/{{ $quotation->reference }}</strong></div>
|
||||
<div>Date: {{ \Carbon\Carbon::parse($quotation->date)->format('d M, Y') }}</div>
|
||||
<div>
|
||||
Status: <strong>{{ $quotation->status }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
Payment Status: <strong>{{ $quotation->payment_status }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="table-responsive-sm" style="margin-top: 30px;">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="align-middle">Product</th>
|
||||
<th class="align-middle">Net Unit Price</th>
|
||||
<th class="align-middle">Quantity</th>
|
||||
<th class="align-middle">Discount</th>
|
||||
<th class="align-middle">Tax</th>
|
||||
<th class="align-middle">Sub Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($quotation->quotationDetails as $item)
|
||||
<tr>
|
||||
<td class="align-middle">
|
||||
{{ $item->product_name }} <br>
|
||||
<span class="badge badge-success">
|
||||
{{ $item->product_code }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="align-middle">{{ format_currency($item->unit_price) }}</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ $item->quantity }}
|
||||
</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ format_currency($item->product_discount_amount) }}
|
||||
</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ format_currency($item->product_tax_amount) }}
|
||||
</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ format_currency($item->sub_total) }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-4 col-xs-offset-8">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="left"><strong>Discount ({{ $quotation->discount_percentage }}%)</strong></td>
|
||||
<td class="right">{{ format_currency($quotation->discount_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Tax ({{ $quotation->tax_percentage }}%)</strong></td>
|
||||
<td class="right">{{ format_currency($quotation->tax_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Shipping)</strong></td>
|
||||
<td class="right">{{ format_currency($quotation->shipping_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Grand Total</strong></td>
|
||||
<td class="right"><strong>{{ format_currency($quotation->total_amount) }}</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 25px;">
|
||||
<div class="col-xs-12">
|
||||
<p style="font-style: italic;text-align: center">{{ settings()->company_name }} © {{ date('Y') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,141 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Create Sale From Quotation')
|
||||
|
||||
@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('quotations.index') }}">Quotations</a></li>
|
||||
<li class="breadcrumb-item active">Make Sale</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 id="sale-form" action="{{ route('sales.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col-lg-4">
|
||||
<div class="form-group">
|
||||
<label for="reference">Reference <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="reference" required readonly value="SL">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="from-group">
|
||||
<div class="form-group">
|
||||
<label for="customer_id">Customer <span class="text-danger">*</span></label>
|
||||
<select class="form-control" name="customer_id" id="customer_id" required>
|
||||
@foreach(\Modules\People\Entities\Customer::all() as $customer)
|
||||
<option {{ $sale->customer_id == $customer->id ? 'selected' : '' }} value="{{ $customer->id }}">{{ $customer->customer_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<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="{{ now()->format('Y-m-d') }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<livewire:product-cart :cartInstance="'sale'" :data="$sale"/>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col-lg-4">
|
||||
<div class="form-group">
|
||||
<label for="status">Status <span class="text-danger">*</span></label>
|
||||
<select class="form-control" name="status" id="status" required>
|
||||
<option value="Pending">Pending</option>
|
||||
<option value="Shipped">Shipped</option>
|
||||
<option value="Completed">Completed</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="from-group">
|
||||
<div class="form-group">
|
||||
<label for="payment_method">Payment Method <span class="text-danger">*</span></label>
|
||||
<select class="form-control" name="payment_method" id="payment_method" required>
|
||||
<option value="Cash">Cash</option>
|
||||
<option value="Credit Card">Credit Card</option>
|
||||
<option value="Bank Transfer">Bank Transfer</option>
|
||||
<option value="Cheque">Cheque</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="form-group">
|
||||
<label for="paid_amount">Amount Received <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<input id="paid_amount" type="text" class="form-control" name="paid_amount" required>
|
||||
<div class="input-group-append">
|
||||
<button id="getTotalAmount" class="btn btn-primary" type="button">
|
||||
<i class="bi bi-check-square"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="note">Note (If Needed)</label>
|
||||
<textarea name="note" id="note" rows="5" class="form-control"></textarea>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="quotation_id" value="{{ $quotation_id }}">
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Create Sale <i class="bi bi-check"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('page_scripts')
|
||||
<script src="{{ asset('js/jquery-mask-money.js') }}"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#paid_amount').maskMoney({
|
||||
prefix:'{{ settings()->currency->symbol }}',
|
||||
thousands:'{{ settings()->currency->thousand_separator }}',
|
||||
decimal:'{{ settings()->currency->decimal_separator }}',
|
||||
allowZero: true,
|
||||
});
|
||||
|
||||
$('#getTotalAmount').click(function () {
|
||||
$('#paid_amount').maskMoney('mask', {{ Cart::instance('sale')->total() }});
|
||||
});
|
||||
|
||||
$('#sale-form').submit(function () {
|
||||
var paid_amount = $('#paid_amount').maskMoney('unmasked')[0];
|
||||
$('#paid_amount').val(paid_amount);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
|
@ -0,0 +1,135 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Quotation Details')
|
||||
|
||||
@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('quotations.index') }}">Quotations</a></li>
|
||||
<li class="breadcrumb-item active">Details</li>
|
||||
</ol>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex flex-wrap align-items-center">
|
||||
<div>
|
||||
Reference: <strong>{{ $quotation->reference }}</strong>
|
||||
</div>
|
||||
<a target="_blank" class="btn btn-sm btn-secondary mfs-auto mfe-1 d-print-none" href="{{ route('quotations.pdf', $quotation->id) }}">
|
||||
<i class="bi bi-printer"></i> Print
|
||||
</a>
|
||||
<a target="_blank" class="btn btn-sm btn-info mfe-1 d-print-none" href="{{ route('quotations.pdf', $quotation->id) }}">
|
||||
<i class="bi bi-save"></i> Save
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-4">
|
||||
<div class="col-sm-4 mb-3 mb-md-0">
|
||||
<h5 class="mb-2 border-bottom pb-2">Company Info:</h5>
|
||||
<div><strong>{{ settings()->company_name }}</strong></div>
|
||||
<div>{{ settings()->company_address }}</div>
|
||||
<div>Email: {{ settings()->company_email }}</div>
|
||||
<div>Phone: {{ settings()->company_phone }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 mb-3 mb-md-0">
|
||||
<h5 class="mb-2 border-bottom pb-2">Customer Info:</h5>
|
||||
<div><strong>{{ $customer->customer_name }}</strong></div>
|
||||
<div>{{ $customer->address }}</div>
|
||||
<div>Email: {{ $customer->customer_email }}</div>
|
||||
<div>Phone: {{ $customer->customer_phone }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 mb-3 mb-md-0">
|
||||
<h5 class="mb-2 border-bottom pb-2">Invoice Info:</h5>
|
||||
<div>Invoice: <strong>INV/{{ $quotation->reference }}</strong></div>
|
||||
<div>Date: {{ \Carbon\Carbon::parse($quotation->date)->format('d M, Y') }}</div>
|
||||
<div>
|
||||
Status: <strong>{{ $quotation->status }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
Payment Status: <strong>{{ $quotation->payment_status }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="align-middle">Product</th>
|
||||
<th class="align-middle">Net Unit Price</th>
|
||||
<th class="align-middle">Quantity</th>
|
||||
<th class="align-middle">Discount</th>
|
||||
<th class="align-middle">Tax</th>
|
||||
<th class="align-middle">Sub Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($quotation->quotationDetails as $item)
|
||||
<tr>
|
||||
<td class="align-middle">
|
||||
{{ $item->product_name }} <br>
|
||||
<span class="badge badge-success">
|
||||
{{ $item->product_code }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="align-middle">{{ format_currency($item->unit_price) }}</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ $item->quantity }}
|
||||
</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ format_currency($item->product_discount_amount) }}
|
||||
</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ format_currency($item->product_tax_amount) }}
|
||||
</td>
|
||||
|
||||
<td class="align-middle">
|
||||
{{ format_currency($item->sub_total) }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-4 col-sm-5 ml-md-auto">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="left"><strong>Discount ({{ $quotation->discount_percentage }}%)</strong></td>
|
||||
<td class="right">{{ format_currency($quotation->discount_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Tax ({{ $quotation->tax_percentage }}%)</strong></td>
|
||||
<td class="right">{{ format_currency($quotation->tax_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Shipping</strong></td>
|
||||
<td class="right">{{ format_currency($quotation->shipping_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Grand Total</strong></td>
|
||||
<td class="right"><strong>{{ format_currency($quotation->total_amount) }}</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -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('/quotation', function (Request $request) {
|
||||
return $request->user();
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
<?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 () {
|
||||
//Generate PDF
|
||||
Route::get('/quotations/pdf/{id}', function ($id) {
|
||||
$quotation = \Modules\Quotation\Entities\Quotation::findOrFail($id);
|
||||
$customer = \Modules\People\Entities\Customer::findOrFail($quotation->customer_id);
|
||||
|
||||
$pdf = \PDF::loadView('quotation::print', [
|
||||
'quotation' => $quotation,
|
||||
'customer' => $customer,
|
||||
])->setPaper('a4');
|
||||
|
||||
return $pdf->stream('quotation-'. $quotation->reference .'.pdf');
|
||||
})->name('quotations.pdf');
|
||||
|
||||
//Send Quotation Mail
|
||||
Route::get('/quotation/mail/{quotation}', 'SendQuotationEmailController')->name('quotation.email');
|
||||
|
||||
//Sales Form Quotation
|
||||
Route::get('/quotation-sales/{quotation}', 'QuotationSalesController')->name('quotation-sales.create');
|
||||
|
||||
//quotations
|
||||
Route::resource('quotations', 'QuotationController');
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "nwidart/quotation",
|
||||
"description": "",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Widart",
|
||||
"email": "n.widart@gmail.com"
|
||||
}
|
||||
],
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [],
|
||||
"aliases": {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Quotation\\": ""
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "Quotation",
|
||||
"alias": "quotation",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"priority": 0,
|
||||
"providers": [
|
||||
"Modules\\Quotation\\Providers\\QuotationServiceProvider"
|
||||
],
|
||||
"aliases": {},
|
||||
"files": [],
|
||||
"requires": []
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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/quotation.js')
|
||||
.sass( __dirname + '/Resources/assets/sass/app.scss', 'css/quotation.css');
|
||||
|
||||
if (mix.inProduction()) {
|
||||
mix.version();
|
||||
}
|
|
@ -115,7 +115,7 @@
|
|||
<td class="right">{{ format_currency($sale->tax_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><strong>Shipping)</strong></td>
|
||||
<td class="left"><strong>Shipping</strong></td>
|
||||
<td class="right">{{ format_currency($sale->shipping_amount) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -13,6 +13,8 @@ class UploadController extends Controller
|
|||
{
|
||||
|
||||
public function filepondUpload(Request $request) {
|
||||
abort_if(!request()->ajax(), 404);
|
||||
|
||||
$request->validate([
|
||||
'image' => 'required|image|mimes:png,jpeg,jpg'
|
||||
]);
|
||||
|
@ -39,6 +41,8 @@ class UploadController extends Controller
|
|||
|
||||
|
||||
public function filepondDelete(Request $request) {
|
||||
abort_if(!request()->ajax(), 404);
|
||||
|
||||
$upload = Upload::where('folder', $request->getContent())->first();
|
||||
|
||||
Storage::deleteDirectory('temp/' . $upload->folder);
|
||||
|
@ -49,6 +53,8 @@ class UploadController extends Controller
|
|||
|
||||
|
||||
public function dropzoneUpload(Request $request) {
|
||||
abort_if(!request()->ajax(), 404);
|
||||
|
||||
$file = $request->file('file');
|
||||
|
||||
$filename = now()->timestamp . '.' . trim($file->getClientOriginalExtension());
|
||||
|
@ -62,6 +68,8 @@ class UploadController extends Controller
|
|||
}
|
||||
|
||||
public function dropzoneDelete(Request $request) {
|
||||
abort_if(!request()->ajax(), 404);
|
||||
|
||||
Storage::delete('temp/dropzone/' . $request->file_name);
|
||||
|
||||
return response()->json($request->file_name, 200);
|
||||
|
|
|
@ -18,7 +18,14 @@ class PermissionsTableSeeder extends Seeder
|
|||
{
|
||||
$permissions = [
|
||||
//User Mangement
|
||||
'edit_own_profile',
|
||||
'access_user_management',
|
||||
//Dashboard
|
||||
'show_total_stats',
|
||||
'show_month_overview',
|
||||
'show_weekly_sales_purchases',
|
||||
'show_monthly_cashflow',
|
||||
'show_notifications',
|
||||
//Products
|
||||
'access_products',
|
||||
'create_products',
|
||||
|
@ -35,6 +42,16 @@ class PermissionsTableSeeder extends Seeder
|
|||
'show_adjustments',
|
||||
'edit_adjustments',
|
||||
'delete_adjustments',
|
||||
//Quotaions
|
||||
'access_quotations',
|
||||
'create_quotations',
|
||||
'show_quotations',
|
||||
'edit_quotations',
|
||||
'delete_quotations',
|
||||
//Create Sale From Quotation
|
||||
'create_quotation_sales',
|
||||
//Send Quotation On Email
|
||||
'send_quotation_mails',
|
||||
//Expenses
|
||||
'access_expenses',
|
||||
'create_expenses',
|
||||
|
|
|
@ -53,6 +53,59 @@
|
|||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Dashboard Permissions -->
|
||||
<div class="col-lg-4 col-md-6 mb-3">
|
||||
<div class="card h-100 border-0 shadow">
|
||||
<div class="card-header">
|
||||
Dashboard
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="show_total_stats" name="permissions[]"
|
||||
value="show_total_stats" {{ $role->hasPermissionTo('show_total_stats') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="show_total_stats">Total Stats</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="show_notifications" name="permissions[]"
|
||||
value="show_notifications" {{ $role->hasPermissionTo('show_notifications') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="show_notifications">Notifications</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="show_month_overview" name="permissions[]"
|
||||
value="show_month_overview" {{ $role->hasPermissionTo('show_month_overview') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="show_month_overview">Month Overview</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="show_weekly_sales_purchases" name="permissions[]"
|
||||
value="show_weekly_sales_purchases" {{ $role->hasPermissionTo('show_weekly_sales_purchases') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="show_weekly_sales_purchases">Weekly Sales & Purchases</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="show_monthly_cashflow" name="permissions[]"
|
||||
value="show_monthly_cashflow" {{ $role->hasPermissionTo('show_monthly_cashflow') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="show_monthly_cashflow">Monthly Cashflow</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- User Management Permission -->
|
||||
<div class="col-lg-4 col-md-6 mb-3">
|
||||
<div class="card h-100 border-0 shadow">
|
||||
|
@ -69,6 +122,14 @@
|
|||
<label class="custom-control-label" for="access_user_management">Access</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="edit_own_profile" name="permissions[]"
|
||||
value="edit_own_profile" {{ $role->hasPermissionTo('edit_own_profile') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="edit_own_profile">Own Profile</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -196,6 +257,75 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quotations Permission -->
|
||||
<div class="col-lg-4 col-md-6 mb-3">
|
||||
<div class="card h-100 border-0 shadow">
|
||||
<div class="card-header">
|
||||
Quotaions
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="access_quotations" name="permissions[]"
|
||||
value="access_quotations" {{ $role->hasPermissionTo('access_quotations') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="access_quotations">Access</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="create_quotations" name="permissions[]"
|
||||
value="create_quotations" {{ $role->hasPermissionTo('create_quotations') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="create_quotations">Create</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="show_quotations" name="permissions[]"
|
||||
value="show_quotations" {{ $role->hasPermissionTo('show_quotations') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="show_quotations">View</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="edit_quotations" name="permissions[]"
|
||||
value="edit_quotations" {{ $role->hasPermissionTo('edit_quotations') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="edit_quotations">Edit</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="delete_quotations" name="permissions[]"
|
||||
value="delete_quotations" {{ $role->hasPermissionTo('delete_quotations') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="delete_quotations">Delete</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="send_quotation_mails" name="permissions[]"
|
||||
value="send_quotation_mails" {{ $role->hasPermissionTo('send_quotation_mails') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="send_quotation_mails">Send Email</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
id="create_quotation_sales" name="permissions[]"
|
||||
value="create_quotation_sales" {{ $role->hasPermissionTo('create_quotation_sales') ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="create_quotation_sales">Sale From Quotation</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Expenses Permission -->
|
||||
<div class="col-lg-4 col-md-6 mb-3">
|
||||
<div class="card h-100 border-0 shadow">
|
||||
|
|
|
@ -20,12 +20,12 @@ class HomeController extends Controller
|
|||
{
|
||||
|
||||
public function index() {
|
||||
$sales = Sale::sum('total_amount');
|
||||
$sale_returns = SaleReturn::sum('total_amount');
|
||||
$purchase_returns = PurchaseReturn::sum('total_amount');
|
||||
$sales = Sale::where('status', 'Completed')->sum('total_amount');
|
||||
$sale_returns = SaleReturn::where('status', 'Completed')->sum('total_amount');
|
||||
$purchase_returns = PurchaseReturn::where('status', 'Completed')->sum('total_amount');
|
||||
$product_costs = 0;
|
||||
|
||||
foreach (Sale::with('saleDetails')->get() as $sale) {
|
||||
foreach (Sale::where('status', 'Completed')->with('saleDetails')->get() as $sale) {
|
||||
foreach ($sale->saleDetails as $saleDetail) {
|
||||
$product_costs += $saleDetail->product->product_cost;
|
||||
}
|
||||
|
@ -46,10 +46,10 @@ class HomeController extends Controller
|
|||
public function currentMonthChart() {
|
||||
abort_if(!request()->ajax(), 404);
|
||||
|
||||
$currentMonthSales = Sale::whereMonth('date', date('m'))
|
||||
$currentMonthSales = Sale::where('status', 'Completed')->whereMonth('date', date('m'))
|
||||
->whereYear('date', date('Y'))
|
||||
->sum('total_amount') / 100;
|
||||
$currentMonthPurchases = Purchase::whereMonth('date', date('m'))
|
||||
$currentMonthPurchases = Purchase::where('status', 'Completed')->whereMonth('date', date('m'))
|
||||
->whereYear('date', date('Y'))
|
||||
->sum('total_amount') / 100;
|
||||
$currentMonthExpenses = Expense::whereMonth('date', date('m'))
|
||||
|
@ -160,7 +160,8 @@ class HomeController extends Controller
|
|||
|
||||
$date_range = Carbon::today()->subDays(6);
|
||||
|
||||
$sales = Sale::where('date', '>=', $date_range)
|
||||
$sales = Sale::where('status', 'Completed')
|
||||
->where('date', '>=', $date_range)
|
||||
->groupBy(DB::raw("DATE_FORMAT(date,'%d-%m-%y')"))
|
||||
->orderBy('date')
|
||||
->get([
|
||||
|
@ -191,7 +192,8 @@ class HomeController extends Controller
|
|||
|
||||
$date_range = Carbon::today()->subDays(6);
|
||||
|
||||
$purchases = Purchase::where('date', '>=', $date_range)
|
||||
$purchases = Purchase::where('status', 'Completed')
|
||||
->where('date', '>=', $date_range)
|
||||
->groupBy(DB::raw("DATE_FORMAT(date,'%d-%m-%y')"))
|
||||
->orderBy('date')
|
||||
->get([
|
||||
|
|
|
@ -43,7 +43,7 @@ class ProductCart extends Component
|
|||
if ($cart_item->options->product_discount_type == 'fixed') {
|
||||
$this->item_discount[$cart_item->id] = $cart_item->options->product_discount;
|
||||
} elseif ($cart_item->options->product_discount_type == 'percentage') {
|
||||
$this->item_discount[$cart_item->id] = 100 * ($cart_item->options->product_discount / $cart_item->price);
|
||||
$this->item_discount[$cart_item->id] = round(100 * ($cart_item->options->product_discount / $cart_item->price));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateNotificationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('notifications', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('type');
|
||||
$table->morphs('notifiable');
|
||||
$table->text('data');
|
||||
$table->timestamp('read_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('notifications');
|
||||
}
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"Purchase": true,
|
||||
"SaleReturn": true,
|
||||
"SalesReturn": true,
|
||||
"PurchasesReturn": true
|
||||
"PurchasesReturn": true,
|
||||
"Quotation": true
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
@can('show_total_stats')
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="card border-0">
|
||||
|
@ -67,8 +68,11 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endcan
|
||||
|
||||
@can('show_weekly_sales_purchases|show_month_overview')
|
||||
<div class="row mb-4">
|
||||
@can('show_weekly_sales_purchases')
|
||||
<div class="col-lg-7">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header">
|
||||
|
@ -79,6 +83,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endcan
|
||||
@can('show_month_overview')
|
||||
<div class="col-lg-5">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header">
|
||||
|
@ -91,8 +97,11 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endcan
|
||||
</div>
|
||||
@endcan
|
||||
|
||||
@can('show_monthly_cashflow')
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
|
@ -105,6 +114,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endcan
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
|
|
@ -10,12 +10,15 @@
|
|||
|
||||
</ul>
|
||||
<ul class="c-header-nav ml-auto mr-4">
|
||||
@can('create_pos_sales')
|
||||
<li class="c-header-nav-item mr-3">
|
||||
<a class="btn btn-primary btn-pill {{ request()->routeIs('app.pos.index') ? 'disabled' : '' }}" href="{{ route('app.pos.index') }}">
|
||||
<i class="bi bi-cart mr-1"></i> POS System
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('show_notifications')
|
||||
<li class="c-header-nav-item dropdown d-md-down-none mr-2">
|
||||
<a class="c-header-nav-link" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="bi bi-bell" style="font-size: 20px;"></i>
|
||||
|
@ -41,6 +44,7 @@
|
|||
@endforelse
|
||||
</div>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
<li class="c-header-nav-item dropdown">
|
||||
<a class="c-header-nav-link" data-toggle="dropdown" href="#" role="button"
|
||||
|
|
|
@ -62,6 +62,28 @@
|
|||
</li>
|
||||
@endcan
|
||||
|
||||
@can('access_quotations')
|
||||
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown {{ request()->routeIs('quotations.*') ? 'c-show' : '' }}">
|
||||
<a class="c-sidebar-nav-link c-sidebar-nav-dropdown-toggle" href="#">
|
||||
<i class="c-sidebar-nav-icon bi bi-cart-check" style="line-height: 1;"></i> Quotations
|
||||
</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('quotations.create') ? 'c-active' : '' }}" href="{{ route('quotations.create') }}">
|
||||
<i class="c-sidebar-nav-icon bi bi-journal-plus" style="line-height: 1;"></i> Create Quotation
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
<li class="c-sidebar-nav-item">
|
||||
<a class="c-sidebar-nav-link {{ request()->routeIs('quotations.index') ? 'c-active' : '' }}" href="{{ route('quotations.index') }}">
|
||||
<i class="c-sidebar-nav-icon bi bi-journals" style="line-height: 1;"></i> All Quotations
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('access_purchases')
|
||||
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown {{ request()->routeIs('purchases*') || request()->routeIs('purchase-payments*') ? 'c-show' : '' }}">
|
||||
<a class="c-sidebar-nav-link c-sidebar-nav-dropdown-toggle" href="#">
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<input wire:model.defer="item_discount.{{ $cart_item->id }}" type="number" class="form-control" value="{{ $item_discount[$cart_item->id] }}" min="0" max="100">
|
||||
@elseif($discount_type[$cart_item->id] == 'fixed')
|
||||
<label>Discount <span class="text-danger">*</span></label>
|
||||
<input wire:model.defer="item_discount.{{ $cart_item->id }}" type="number" class="form-control" value="{{ $item_discount[$cart_item->id] }}" step="0.01">
|
||||
<input wire:model.defer="item_discount.{{ $cart_item->id }}" type="number" class="form-control" value="{{ $item_discount[$cart_item->id] }}">
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue