File "PurchaseOrderApiController.php"
Full Path: /home/clickysoft/public_html/jmapi5.clickysoft.net/app/Http/Controllers/Api/V1/Admin/PurchaseOrderApiController.php
File size: 25.83 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace App\Http\Controllers\Api\V1\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\PurchaseOrderReportRequest;
use App\Http\Requests\Admin\StorePurchaseOrderRequest;
use App\Http\Requests\Admin\UpdatePurchaseOrderAdminNotesRequest;
use App\Http\Resources\Admin\PurchaseOrderDetailsResource;
use App\Http\Resources\Admin\PurchaseOrderResource;
use App\Mail\PurchaseOrderMail;
use App\Models\OfficeSupplies;
use App\Models\Order;
use App\Models\OrderItems;
use App\Models\Product;
use App\Models\ProductPrice;
use App\Models\ProductVariationCombination;
use App\Models\PurchaseOrder;
use App\Models\PurchaseOrderDetails;
use App\Models\PurchaseOrderOrderDetails;
use App\Models\PurchaseOrderToCreate;
use App\Models\SiteSetting;
use App\Models\Status;
use App\Models\Variation;
use App\Models\Vendor;
use App\Notifications\OrderStatusUpdatedNotification;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Facades\Gate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\Response;
class PurchaseOrderApiController extends Controller
{
public function index(Request $request)
{
abort_if(Gate::denies('purchase_order_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
$request->validate([
'report_type' => 'nullable|in:' . implode(',', array_keys(PurchaseOrder::REPORTS_MODE))
]);
$report_scope = PurchaseOrder::REPORTS_MODE[$request->get('report_type') ?? "current_year"];
$purchase_orders = PurchaseOrder::when($request->filled('order_number'), function ($query) use ($request) {
$query->where('order_number', 'like', "%{$request->get('order_number')}%");
})->when($request->filled('status'), function ($query) use ($request) {
$query->where('status', 'like', "%{$request->get('status')}%");
})->when($request->filled('vendor'), function ($query) use ($request) {
$query->whereHas('vendor', function ($query) use ($request) {
$query->where('name', 'like', "%{$request->get('vendor')}%");
$query->orWhere('id', $request->get('category'));
});
})->when($request->filled('product_number'), function ($query) use ($request) {
$query->whereHas('orderDetails', function ($query) use ($request) {
$query->where('product_number', 'like', "%{$request->get('product_number')}%");
});
})->$report_scope()
->orderBy('created_at', 'DESC')->paginate(50);
$total_amount = $purchase_orders->sum('total_price');
PurchaseOrderResource::withoutWrapping();
return PurchaseOrderResource::collection($purchase_orders)->additional(["purchase_orders_total" => "$" . number_format($total_amount, 2)]);
}
public function get_purchase_order_items()
{
abort_if(Gate::denies('purchase_order_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
/*$data = PurchaseOrderToCreate::select('*', DB::raw('SUM(quantity) as total_quantity'))
->groupBy('product_id')
->groupBy('price_id')
->get();*/
$data = PurchaseOrderToCreate::select('product_id', 'price_id', DB::raw('SUM(quantity) as total_quantity'))
->groupBy('price_id')
->get();
return \response()->json(['data' => $this->getPurchaseItemsToCreate($data)], Response::HTTP_OK);
}
public function get_shipping_speed()
{
return \response()
->json(['data' => PurchaseOrder::SHIPPING_SPEED], Response::HTTP_OK);
}
public function get_po_statuses()
{
return \response()
->json(['data' => PurchaseOrder::PURCHASE_ORDER_STATUS], Response::HTTP_OK);
}
public function get_payment_terms()
{
return \response()
->json(['data' => PurchaseOrder::PAYMENT_TERMS], Response::HTTP_OK);
}
public function get_po_addresses()
{
$shipping_address = SiteSetting::where('key', 'Shipping Address')->first();
$billing_address = SiteSetting::where('key', 'Billing Address')->first();
return \response()
->json(['data' => [
'shipping_address' => $shipping_address->value ?? '',
'billing_address' => $billing_address->value ?? '',
]], Response::HTTP_OK);
}
public function change_po_status(Request $request, PurchaseOrder $purchaseOrder)
{
abort_if(Gate::denies('purchase_order_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
if ($purchaseOrder->status == 'Received') {
return \response()->json([
'message' => 'Purchase order already marked as received.',
'errors' => ['error' => ['Purchase order already marked as received']],
])
->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
} else {
/*$request->validate([
'stock_location_id' => 'required|exists:stock_locations,id',
]);*/
$purchaseOrder->status = 'Received';
// $purchaseOrder->stock_location_id = $request->get('stock_location_id');
$purchaseOrder->save();
return (new PurchaseOrderResource($purchaseOrder))
->response()
->setStatusCode(Response::HTTP_CREATED);
}
}
public function store(StorePurchaseOrderRequest $request)
{
try {
DB::beginTransaction();
foreach ($request->products ?? [] as $product) {
if ($product['product_type'] == 'configurable' && $product['variation_id'] != null) {
$product_exists = Variation::find($product['variation_id']);
} else {
$product_exists = Product::find($product['product_id']);
}
$total_product_quantity = 0;
foreach ($product['orders'] as $p) {
if (isset($p['quantity'])) {
$total_product_quantity += (int) $p['quantity'];
}
if (isset($p['extra_quantity'])) {
$total_product_quantity += (int) $p['extra_quantity'];
}
}
$product['total_quantity'] = $total_product_quantity;
if ($product_exists) {
//Here we will get vendor depending on product type
$vendor_id = $product_exists->vendor_id;
if (isset($data[$vendor_id])) {
$data[$vendor_id]['products'][] = $product;
} else {
$data[$vendor_id] = [
'ordered_by_id' => auth()->id(),
'order_number' => 00,
'payment_terms' => $request->payment_terms,
'reference' => $request->reference,
'special_notes' => $request->special_notes,
'shipping_address' => $request->shipping_address,
'billing_address' => $request->billing_address ?? 'N/A',
'vendor_id' => $vendor_id,
'status' => 'Pending',
'products' => [$product],
];
}
}
}
//Handle office supplies products
foreach ($request->office_supplies ?? [] as $office_supply) {
$office_supply['product_type'] = "office_supplies";
if (isset($data[$office_supply['vendor_id']])) {
$data[$office_supply['vendor_id']]['office_products'][] = $office_supply;
} else {
$data[$office_supply['vendor_id']] = [
'ordered_by_id' => auth()->id(),
'order_number' => 00,
'payment_terms' => $request->payment_terms,
'reference' => $request->reference,
'special_notes' => $request->special_notes,
'shipping_address' => $request->shipping_address,
'billing_address' => $request->billing_address ?? 'N/A',
'vendor_id' => $office_supply['vendor_id'],
'status' => 'Pending',
'office_products' => [$office_supply],
];
}
}
$data = $this->preparePurchaseOrder($data ?? []);
$this->deletePurchaseOrderItemsCreated($request->products ?? []);
DB::commit();
PurchaseOrderResource::withoutWrapping();
return (PurchaseOrderResource::collection($data ?? []))
->response()
->setStatusCode(Response::HTTP_CREATED);
} catch (\Exception $e) {
Log::channel('db_errors')->info('Purchase Order Creation Error');
Log::channel('db_errors')->info($e->getMessage() . " at line : " . $e->getLine());
return response()
->json([
'message' => "Can not create purchase order.",
'errors' => ["error" => ["Unable to create purchase order."]]
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
public function show(PurchaseOrder $purchaseOrder)
{
abort_if(Gate::denies('purchase_order_show'), Response::HTTP_FORBIDDEN, '403 Forbidden');
return (new PurchaseOrderDetailsResource($purchaseOrder))
->response()
->setStatusCode(Response::HTTP_CREATED);
}
public function update(Request $request, PurchaseOrder $purchaseOrder)
{
//
}
public function editAdminNotes(UpdatePurchaseOrderAdminNotesRequest $request, PurchaseOrder $purchaseOrder)
{
$purchaseOrder->admin_notes = $request->admin_notes;
$purchaseOrder->save();
return response()
->json(['message' => 'Admin notes updated successfully'], Response::HTTP_OK);
}
public function destroy(PurchaseOrder $purchaseOrder)
{
abort_if(Gate::denies('purchase_order_delete'), Response::HTTP_FORBIDDEN, '403 Forbidden');
abort_if($purchaseOrder->status != 'Received', Response::HTTP_FORBIDDEN, 'Can not delete pending purchase orders');
try {
$purchaseOrder->delete();
return response()
->json(['message' => 'Purchase order deleted successfully'], Response::HTTP_OK);
} catch (\Exception $e) {
Log::channel('db_errors')->info('Record Deletion Error : Purchase Order -> ' . $purchaseOrder->id);
Log::channel('db_errors')->info($e->getMessage());
return response()
->json([
'message' => "Record not deleted.",
'errors' => ["error" => ["Unable to delete purchase order."]]
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
public function preparePurchaseOrder($data)
{
$mail_data = $purchase_orders = [];
foreach ($data ?? [] as $datum) {
$total_quantity = $total_amount = 0;
$office_products_total_quantity = $office_products_total_amount = 0;
$vendor = Vendor::find($datum['vendor_id']);
$purchase_orders[] = $purchase_order = PurchaseOrder::create($datum);
foreach ($datum['products'] ?? [] as $product) {
$product_price = ProductPrice::find($product['price_id']);
$variation_price = Variation::find($product['variation_id']);
if ($variation_price) {
$po_details[] = $po_detail = [
'purchase_order_id' => $purchase_order->id,
'product_id' => $variation_price->id,
'product_number' => $variation_price->sku,
'price_id' => $product_price->id,
'quantity' => $product['total_quantity'],
'product_type' => $product['product_type'],
'price' => $variation_price->vendor_price,
'total_price' => $product['total_quantity'] * $variation_price->vendor_price,
];
} else {
$po_details[] = $po_detail = [
'purchase_order_id' => $purchase_order->id,
'product_id' => $product['product_id'],
'product_number' => $product_price->supplier_prod_number,
'price_id' => $product_price->id,
'quantity' => $product['total_quantity'],
'product_type' => $product['product_type'],
'price' => $product_price->vendor_price,
'total_price' => $product['total_quantity'] * $product_price->vendor_price,
];
}
$purchase_order_details = PurchaseOrderDetails::create($po_detail);
$total_quantity += $po_detail['quantity'];
$total_amount += $po_detail['quantity'] * $po_detail['price'];
foreach ($product['orders'] as $po_order) {
$po_order_data = [
'purchase_order_details_id' => $purchase_order_details->id
];
if (isset($po_order['order_id'])) {
$po_order_data['order_id'] = $po_order['order_id'];
$po_order_data['quantity'] = $po_order['quantity'];
}
if (isset($po_order['extra_quantity'])) {
$po_order_data['order_id'] = 0;
$po_order_data['quantity'] = $po_order['extra_quantity'];
}
PurchaseOrderOrderDetails::create($po_order_data);
}
}
foreach ($datum['office_products'] ?? [] as $office_product) {
$db_office_product = OfficeSupplies::find($office_product['product_id']);
$os_details[] = $os_detail = [
'purchase_order_id' => $purchase_order->id,
'product_id' => $office_product['product_id'],
'product_number' => $db_office_product->sku,
'quantity' => $office_product['quantity'],
'price' => $db_office_product->price,
'product_type' => $office_product['product_type'],
'total_price' => $office_product['quantity'] * $db_office_product->price,
];
$office_products_total_quantity += $office_product['quantity'];
$office_products_total_amount += $office_product['quantity'] * $db_office_product->price;
PurchaseOrderDetails::create($os_detail);
}
$purchase_order->total_quantity = $total_quantity + $office_products_total_quantity;
$purchase_order->total_price = $total_amount + $office_products_total_amount;
$purchase_order->save();
//Update PO number
$purchase_order->order_number = (new Order)->str_random($purchase_order->id);
$purchase_order->save();
$mail_data['p_order'] = $purchase_order;
$mail_data['account_number'] = $vendor->account_number;
$mail_data['p_order_products'] = $po_details ?? [];
$mail_data['os_products'] = $os_details ?? [];
$mail_data['secondary_email'] = $vendor->secondary_email;
$path = $purchase_order->generateAttachment($mail_data);
$mail_data['attachment_path'] = $path;
//Send Mail to each vendor
try {
Mail::to($vendor->email)->send(new PurchaseOrderMail($mail_data));
} catch (\Exception $e) {
Log::channel('info_errors')->info('Mail Error');
Log::channel('info_errors')->info($e->getMessage());
}
}
return $purchase_orders;
}
public function getPurchaseItemsToCreate($data)
{
$products = [];
foreach ($data as $datum) {
$orders = [];
$vs = ProductVariationCombination::where('product_price_id', $datum->price_id)->get();
$bd_product = Product::find($datum->product_id);
$price_id = ProductPrice::find($datum->price_id);
$variations = [];
foreach ($vs ?? [] as $variation) {
$variations[] = [
'type' => $variation->variation?->type,
'value' => $variation->variation?->value,
];
}
foreach ($datum->orders as $order_item) {
$order = $order_item->order;
if (isset($orders[$order->id])) {
$orders[$order->id]['quantity'] += $order_item->quantity;
} else {
$orders[$order->id] = [
'id' => $order->id,
'order_number' => $order->order_number,
'quantity' => $order_item->quantity,
];
}
}
if ($bd_product->product_type == 'configurable') {
$price_variations_ids = ProductVariationCombination::where('product_price_id', $datum->price_id)
->get()
->pluck('variation_id')
->toArray();
$price_variations = Variation::whereIn('id', $price_variations_ids)->get();
foreach ($price_variations as $price_variation) {
$products[] = [
'product_type' => 'configurable',
'variation_id' => $price_variation->id,
'product_id' => $bd_product->id,
'price_id' => $datum->price_id,
'product_name' => $price_variation->value,
'vendor_name' => $price_variation->vendor->name ?? "",
'product_number' => $price_variation->sku,
'vendor_price' => $price_variation->vendor_price,
'total_quantity' => $datum->total_quantity,
'total_price' => '$' . number_format($price_variation->vendor_price * $datum->total_quantity, 2),
'featured_image' => ($image = $bd_product->featured_image) ? [
'url' => $image->url,
'preview' => $image->preview,
'thumbnail' => $image->thumbnail,
] : '',
'variations' => $variations,
'orders' => $orders ?? [],
];
}
} else {
if ($price_id != null) {
$products[] = [
'product_type' => 'standard',
'variation_id' => null,
'product_id' => $bd_product->id,
'price_id' => $datum->price_id,
'product_name' => $bd_product->name,
'vendor_name' => $bd_product->vendor->name ?? "",
'product_number' => $price_id->supplier_prod_number,
'vendor_price' => $price_id->vendor_price,
'total_quantity' => $datum->total_quantity,
'total_price' => '$' . number_format($price_id->vendor_price * $datum->quantity, 2),
'featured_image' => ($image = $bd_product->featured_image) ? [
'url' => $image->url,
'preview' => $image->preview,
'thumbnail' => $image->thumbnail,
] : '',
'variations' => $variations,
'orders' => $orders ?? [],
];
}
}
}
return $products;
}
public function sendOrderChangeMail(Order $order, Status $product_ordered_status, Status $product_received_status): void
{
$data = [
'order_number' => $order->order_number,
'customer_name' => $order->user->name ?? '',
'previous_order_status' => $product_ordered_status->name,
'current_order_status' => $product_received_status->name,
'notes' => null,
];
$order->user->notify((new OrderStatusUpdatedNotification($data))->delay(now()->addSeconds(5)));
}
public function poReport(PurchaseOrderReportRequest $request)
{
$date_range = reportDateRange($request);
$startYear = $date_range['year_from']->format('Y');
$endYear = $date_range['year_to']->format('Y');
$group_vendors = $request->get('group_vendors') ?? false;
if ($group_vendors) {
$data = DB::table('purchase_orders as p')
->select('vendors.name as vendor_name', DB::raw('SUM(p.total_quantity) as quantity'), DB::raw('SUM(p.total_price) as total'))
->join('vendors', 'p.vendor_id', '=', 'vendors.id')
->whereBetween('p.created_at', [$startYear . '-01-01', $endYear . '-12-31'])
->groupBy('vendor_id')
->get();
return $this->generateGroupedReport($data, year_from: $startYear, year_to: $endYear);
} else {
$data = DB::table('purchase_orders as p')
->select('p.*', 'vendors.name as vendor_name', 'users.name as ordered_by')
->join('vendors', 'p.vendor_id', '=', 'vendors.id')
->join('users', 'p.ordered_by_id', '=', 'users.id')
->whereBetween('p.created_at', [$startYear . '-01-01', $endYear . '-12-31'])
->orderByDesc('created_at')
->get();
return $this->generateNonGroupedReport($data, year_from: $startYear, year_to: $endYear);
}
}
public function generateGroupedReport($reports, $year_from = "", $year_to = "")
{
$path = "storage/reports/yoy-grouped-report.pdf";
if (!is_dir(public_path('storage/reports'))) {
Storage::disk('public')->makeDirectory('reports');
}
Pdf::loadView('layouts.reports.yoy-grouped', compact('reports', 'year_from', 'year_to'))
->save(public_path($path));
return \response()->json(['data' => asset($path)])->setStatusCode(Response::HTTP_OK);
}
public function generateNonGroupedReport($reports, $year_from = "", $year_to = "")
{
$report_filtered = [];
$totals = [];
foreach ($reports as $report) {
$report_filtered[$report->vendor_name][] = [
'id' => $report->id,
'ordered_by' => $report->ordered_by,
'order_number' => $report->order_number,
'payment_terms' => $report->payment_terms,
'reference' => $report->reference,
'vendor_name' => $report->vendor_name,
'quantity' => $report->total_quantity,
'price' => $report->total_price,
'created_at' => $report->created_at,
];
isset($totals[$report->vendor_name]['total_quantity'])
? $totals[$report->vendor_name]['total_quantity'] += $report->total_quantity
: $totals[$report->vendor_name]['total_quantity'] = $report->total_quantity;
isset($totals[$report->vendor_name]['total_price'])
? $totals[$report->vendor_name]['total_price'] += $report->total_price
: $totals[$report->vendor_name]['total_price'] = $report->total_price;
}
$path = "storage/reports/yoy-non-grouped-report.pdf";
if (!is_dir(public_path('storage/reports'))) {
Storage::disk('public')->makeDirectory('reports');
}
Pdf::loadView('layouts.reports.yoy-non-grouped', compact('report_filtered', 'totals', 'year_from', 'year_to'))
->setPaper('A4', 'landscape')
->save(public_path($path));
return \response()->json(['data' => asset($path)])->setStatusCode(Response::HTTP_OK);
}
public function deletePurchaseOrderItemsCreated($products)
{
foreach ($products as $product) {
foreach ($product['orders'] as $order) {
if (isset($order['order_id'])) {
//Update PO flag in order items
OrderItems::where([
'product_id' => $product['product_id'],
'price_id' => $product['price_id'],
'order_id' => $order['order_id'],
])->update([
'po_created' => 1,
]);
PurchaseOrderToCreate::where([
'product_id' => $product['product_id'],
'price_id' => $product['price_id'],
'order_id' => $order['order_id'],
])->delete();
}
}
}
}
public function removePOItem(Request $request)
{
$request->validate([
'product_id' => 'required',
'price_id' => 'required',
'order_id' => 'required',
]);
//Update PO flag in order items
OrderItems::where([
'product_id' => $request->get('product_id'),
'price_id' => $request->get('price_id'),
'order_id' => $request->get('order_id'),
])->update([
'po_created' => 1,
]);
PurchaseOrderToCreate::where([
'product_id' => $request->get('product_id'),
'price_id' => $request->get('price_id'),
'order_id' => $request->get('order_id'),
])->delete();
return response()
->json(['message' => 'Purchase order item deleted successfully'], Response::HTTP_OK);
}
}