ultimatepos/app/Http/Controllers/StockAdjustmentController.php

521 lines
21 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\BusinessLocation;
use App\PurchaseLine;
use App\Transaction;
use App\Utils\ModuleUtil;
use App\Utils\ProductUtil;
use App\Utils\TransactionUtil;
use Datatables;
use DB;
use Illuminate\Http\Request;
use Spatie\Activitylog\Models\Activity;
use App\Events\StockAdjustmentCreatedOrModified;
class StockAdjustmentController extends Controller
{
/**
* All Utils instance.
*/
protected $productUtil;
protected $transactionUtil;
protected $moduleUtil;
/**
* Constructor
*
* @param ProductUtils $product
* @return void
*/
public function __construct(ProductUtil $productUtil, TransactionUtil $transactionUtil, ModuleUtil $moduleUtil)
{
$this->productUtil = $productUtil;
$this->transactionUtil = $transactionUtil;
$this->moduleUtil = $moduleUtil;
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
if (! auth()->user()->can('stock_adjustment.view') && ! auth()->user()->can('stock_adjustment.create') && ! auth()->user()->can('view_own_stock_adjustment')) {
abort(403, 'Unauthorized action.');
}
if (request()->ajax()) {
$business_id = request()->session()->get('user.business_id');
$stock_adjustments = Transaction::join(
'business_locations AS BL',
'transactions.location_id',
'=',
'BL.id'
)
->leftJoin('users as u', 'transactions.created_by', '=', 'u.id')
->where('transactions.business_id', $business_id)
->where('transactions.type', 'stock_adjustment')
->select(
'transactions.id',
'transaction_date',
'ref_no',
'BL.name as location_name',
'adjustment_type',
'final_total',
'total_amount_recovered',
'additional_notes',
'transactions.id as DT_RowId',
DB::raw("CONCAT(COALESCE(u.surname, ''),' ',COALESCE(u.first_name, ''),' ',COALESCE(u.last_name,'')) as added_by")
);
$permitted_locations = auth()->user()->permitted_locations();
if ($permitted_locations != 'all') {
$stock_adjustments->whereIn('transactions.location_id', $permitted_locations);
}
$hide = '';
$start_date = request()->get('start_date');
$end_date = request()->get('end_date');
if (! empty($start_date) && ! empty($end_date)) {
$stock_adjustments->whereBetween(DB::raw('date(transaction_date)'), [$start_date, $end_date]);
$hide = 'hide';
}
$location_id = request()->get('location_id');
if (! empty($location_id)) {
$stock_adjustments->where('transactions.location_id', $location_id);
}
if (! auth()->user()->can('stock_adjustment.view') && auth()->user()->can('view_own_stock_adjustment')) {
$stock_adjustments->where('transactions.created_by', request()->session()->get('user.id'));
}
if(! auth()->user()->can('stock_adjustment.delete')){
$hide = 'hide';
}
return Datatables::of($stock_adjustments)
->addColumn('action', '<button type="button" data-href="{{action([\App\Http\Controllers\StockAdjustmentController::class, \'show\'], [$id]) }}" class="tw-dw-btn tw-dw-btn-xs tw-dw-btn-outline tw-dw-btn-primary btn-modal" data-container=".view_modal"><i class="fa fa-eye" aria-hidden="true"></i> @lang("messages.view")</button>
&nbsp;
<button type="button" data-href="{{ action([\App\Http\Controllers\StockAdjustmentController::class, \'destroy\'], [$id]) }}" class="tw-dw-btn tw-dw-btn-xs tw-dw-btn-outline tw-dw-btn-error delete_stock_adjustment '.$hide.'"><i class="fa fa-trash" aria-hidden="true"></i> @lang("messages.delete")</button>')
->removeColumn('id')
->editColumn(
'final_total',
function ($row) {
if (auth()->user()->can('view_purchase_price')) {
return $this->transactionUtil->num_f($row->final_total, true);
} else {
return '<span>-</span>';
}
}
)
->editColumn(
'total_amount_recovered',
function ($row) {
if (auth()->user()->can('view_purchase_price')) {
return $this->transactionUtil->num_f($row->total_amount_recovered, true);
} else {
return '<span>-</span>';
}
}
)
->editColumn('transaction_date', '@format_datetime($transaction_date)')
->editColumn('adjustment_type', function ($row) {
return __('stock_adjustment.'.$row->adjustment_type);
})
->setRowAttr([
'data-href' => function ($row) {
return action([\App\Http\Controllers\StockAdjustmentController::class, 'show'], [$row->id]);
}, ])
->rawColumns(['final_total', 'action', 'total_amount_recovered'])
->make(true);
}
return view('stock_adjustment.index');
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
if (! auth()->user()->can('stock_adjustment.create')) {
abort(403, 'Unauthorized action.');
}
$business_id = request()->session()->get('user.business_id');
//Check if subscribed or not
if (! $this->moduleUtil->isSubscribed($business_id)) {
return $this->moduleUtil->expiredResponse(action([\App\Http\Controllers\StockAdjustmentController::class, 'index']));
}
$business_locations = BusinessLocation::forDropdown($business_id);
return view('stock_adjustment.create')
->with(compact('business_locations'));
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
if (! auth()->user()->can('stock_adjustment.create')) {
abort(403, 'Unauthorized action.');
}
try {
DB::beginTransaction();
$input_data = $request->only(['location_id', 'transaction_date', 'adjustment_type', 'additional_notes', 'total_amount_recovered', 'final_total', 'ref_no']);
$business_id = $request->session()->get('user.business_id');
//Check if subscribed or not
if (! $this->moduleUtil->isSubscribed($business_id)) {
return $this->moduleUtil->expiredResponse(action([\App\Http\Controllers\StockAdjustmentController::class, 'index']));
}
$user_id = $request->session()->get('user.id');
$input_data['type'] = 'stock_adjustment';
$input_data['business_id'] = $business_id;
$input_data['created_by'] = $user_id;
$input_data['transaction_date'] = $this->productUtil->uf_date($input_data['transaction_date'], true);
$input_data['total_amount_recovered'] = $this->productUtil->num_uf($input_data['total_amount_recovered']);
//Update reference count
$ref_count = $this->productUtil->setAndGetReferenceCount('stock_adjustment');
//Generate reference number
if (empty($input_data['ref_no'])) {
$input_data['ref_no'] = $this->productUtil->generateReferenceNumber('stock_adjustment', $ref_count);
}
$products = $request->input('products');
if (! empty($products)) {
$product_data = [];
foreach ($products as $product) {
$adjustment_line = [
'product_id' => $product['product_id'],
'variation_id' => $product['variation_id'],
'quantity' => $this->productUtil->num_uf($product['quantity']),
'unit_price' => $this->productUtil->num_uf($product['unit_price']),
];
if (! empty($product['lot_no_line_id'])) {
//Add lot_no_line_id to stock adjustment line
$adjustment_line['lot_no_line_id'] = $product['lot_no_line_id'];
}
$product_data[] = $adjustment_line;
//Decrease available quantity
$this->productUtil->decreaseProductQuantity(
$product['product_id'],
$product['variation_id'],
$input_data['location_id'],
$this->productUtil->num_uf($product['quantity'])
);
}
$stock_adjustment = Transaction::create($input_data);
$stock_adjustment->stock_adjustment_lines()->createMany($product_data);
//Map Stock adjustment & Purchase.
$business = ['id' => $business_id,
'accounting_method' => $request->session()->get('business.accounting_method'),
'location_id' => $input_data['location_id'],
];
$this->transactionUtil->mapPurchaseSell($business, $stock_adjustment->stock_adjustment_lines, 'stock_adjustment');
event(new StockAdjustmentCreatedOrModified($stock_adjustment, 'added'));
$this->transactionUtil->activityLog($stock_adjustment, 'added', null, [], false);
}
$output = ['success' => 1,
'msg' => __('stock_adjustment.stock_adjustment_added_successfully'),
];
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
\Log::emergency('File:'.$e->getFile().'Line:'.$e->getLine().'Message:'.$e->getMessage());
$msg = trans('messages.something_went_wrong');
if (get_class($e) == \App\Exceptions\PurchaseSellMismatch::class) {
$msg = $e->getMessage();
}
$output = ['success' => 0,
'msg' => $msg,
];
}
return redirect('stock-adjustments')->with('status', $output);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
if (! auth()->user()->can('stock_adjustment.view')) {
abort(403, 'Unauthorized action.');
}
$business_id = request()->session()->get('user.business_id');
$stock_adjustment = Transaction::where('transactions.business_id', $business_id)
->where('transactions.id', $id)
->where('transactions.type', 'stock_adjustment')
->with(['stock_adjustment_lines', 'location', 'business', 'stock_adjustment_lines.variation', 'stock_adjustment_lines.variation.product', 'stock_adjustment_lines.variation.product_variation', 'stock_adjustment_lines.lot_details'])
->first();
$lot_n_exp_enabled = false;
if (request()->session()->get('business.enable_lot_number') == 1 || request()->session()->get('business.enable_product_expiry') == 1) {
$lot_n_exp_enabled = true;
}
$activities = Activity::forSubject($stock_adjustment)
->with(['causer', 'subject'])
->latest()
->get();
return view('stock_adjustment.show')
->with(compact('stock_adjustment', 'lot_n_exp_enabled', 'activities'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Transaction $stockAdjustment
* @return \Illuminate\Http\Response
*/
public function edit(Transaction $stockAdjustment)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Transaction $stockAdjustment
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Transaction $stockAdjustment)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
if (! auth()->user()->can('stock_adjustment.delete')) {
abort(403, 'Unauthorized action.');
}
try {
if (request()->ajax()) {
DB::beginTransaction();
$stock_adjustment = Transaction::where('id', $id)
->where('type', 'stock_adjustment')
->with(['stock_adjustment_lines'])
->first();
//Add deleted product quantity to available quantity
$stock_adjustment_lines = $stock_adjustment->stock_adjustment_lines;
if (! empty($stock_adjustment_lines)) {
$line_ids = [];
foreach ($stock_adjustment_lines as $stock_adjustment_line) {
$this->productUtil->updateProductQuantity(
$stock_adjustment->location_id,
$stock_adjustment_line->product_id,
$stock_adjustment_line->variation_id,
$this->productUtil->num_f($stock_adjustment_line->quantity)
);
$line_ids[] = $stock_adjustment_line->id;
}
$this->transactionUtil->mapPurchaseQuantityForDeleteStockAdjustment($line_ids);
}
$stock_adjustment->delete();
event( new StockAdjustmentCreatedOrModified($stock_adjustment, 'deleted'));
//Remove Mapping between stock adjustment & purchase.
$output = ['success' => 1,
'msg' => __('stock_adjustment.delete_success'),
];
DB::commit();
}
} catch (\Exception $e) {
DB::rollBack();
\Log::emergency('File:'.$e->getFile().'Line:'.$e->getLine().'Message:'.$e->getMessage());
$output = ['success' => 0,
'msg' => __('messages.something_went_wrong'),
];
}
return $output;
}
/**
* Return product rows
*
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function getProductRow(Request $request)
{
if (request()->ajax()) {
$row_index = $request->input('row_index');
$variation_id = $request->input('variation_id');
$location_id = $request->input('location_id');
$business_id = $request->session()->get('user.business_id');
$product = $this->productUtil->getDetailsFromVariation($variation_id, $business_id, $location_id);
$product->formatted_qty_available = $this->productUtil->num_f($product->qty_available);
$type = ! empty($request->input('type')) ? $request->input('type') : 'stock_adjustment';
//Get lot number dropdown if enabled
$lot_numbers = [];
if (request()->session()->get('business.enable_lot_number') == 1 || request()->session()->get('business.enable_product_expiry') == 1) {
$lot_number_obj = $this->transactionUtil->getLotNumbersFromVariation($variation_id, $business_id, $location_id, true);
foreach ($lot_number_obj as $lot_number) {
$lot_number->qty_formated = $this->productUtil->num_f($lot_number->qty_available);
$lot_numbers[] = $lot_number;
}
}
$product->lot_numbers = $lot_numbers;
$sub_units = $this->productUtil->getSubUnits($business_id, $product->unit_id, false, $product->id);
if ($type == 'stock_transfer') {
return view('stock_transfer.partials.product_table_row')
->with(compact('product', 'row_index', 'sub_units'));
} else {
return view('stock_adjustment.partials.product_table_row')
->with(compact('product', 'row_index', 'sub_units'));
}
}
}
/**
* Sets expired purchase line as stock adjustmnet
*
* @param int $purchase_line_id
* @return json $output
*/
public function removeExpiredStock($purchase_line_id)
{
if (! auth()->user()->can('stock_adjustment.delete')) {
abort(403, 'Unauthorized action.');
}
try {
$purchase_line = PurchaseLine::where('id', $purchase_line_id)
->with(['transaction'])
->first();
if (! empty($purchase_line)) {
DB::beginTransaction();
$qty_unsold = $purchase_line->quantity - $purchase_line->quantity_sold - $purchase_line->quantity_adjusted - $purchase_line->quantity_returned;
$final_total = $purchase_line->purchase_price_inc_tax * $qty_unsold;
$user_id = request()->session()->get('user.id');
$business_id = request()->session()->get('user.business_id');
//Update reference count
$ref_count = $this->productUtil->setAndGetReferenceCount('stock_adjustment');
$stock_adjstmt_data = [
'type' => 'stock_adjustment',
'business_id' => $business_id,
'created_by' => $user_id,
'transaction_date' => \Carbon::now()->format('Y-m-d'),
'total_amount_recovered' => 0,
'location_id' => $purchase_line->transaction->location_id,
'adjustment_type' => 'normal',
'final_total' => $final_total,
'ref_no' => $this->productUtil->generateReferenceNumber('stock_adjustment', $ref_count),
];
//Create stock adjustment transaction
$stock_adjustment = Transaction::create($stock_adjstmt_data);
$stock_adjustment_line = [
'product_id' => $purchase_line->product_id,
'variation_id' => $purchase_line->variation_id,
'quantity' => $qty_unsold,
'unit_price' => $purchase_line->purchase_price_inc_tax,
'removed_purchase_line' => $purchase_line->id,
];
//Create stock adjustment line with the purchase line
$stock_adjustment->stock_adjustment_lines()->create($stock_adjustment_line);
//Decrease available quantity
$this->productUtil->decreaseProductQuantity(
$purchase_line->product_id,
$purchase_line->variation_id,
$purchase_line->transaction->location_id,
$qty_unsold
);
//Map Stock adjustment & Purchase.
$business = ['id' => $business_id,
'accounting_method' => request()->session()->get('business.accounting_method'),
'location_id' => $purchase_line->transaction->location_id,
];
$this->transactionUtil->mapPurchaseSell($business, $stock_adjustment->stock_adjustment_lines, 'stock_adjustment', false, $purchase_line->id);
DB::commit();
$output = ['success' => 1,
'msg' => __('lang_v1.stock_removed_successfully'),
];
}
} catch (\Exception $e) {
DB::rollBack();
\Log::emergency('File:'.$e->getFile().'Line:'.$e->getLine().'Message:'.$e->getMessage());
$msg = trans('messages.something_went_wrong');
if (get_class($e) == \App\Exceptions\PurchaseSellMismatch::class) {
$msg = $e->getMessage();
}
$output = ['success' => 0,
'msg' => $msg,
];
}
return $output;
}
}