middleware('check.permission:purchases.read')->only(['index']); $this->middleware('check.permission:purchases.create')->only(['create', 'store']); $this->middleware('check.permission:purchases.update')->only(['edit', 'update']); $this->middleware('check.permission:purchases.delete')->only(['destroy', 'deleteAll']); } public function index(Request $request) { $business_id = auth()->user()->business_id; $purchasesWithReturns = PurchaseReturn::where('business_id', $business_id) ->pluck('purchase_id') ->toArray(); $purchasesQuery = Purchase::with('user:id,name', 'party:id,name,email,phone,type', 'payment_type:id,name', 'branch:id,name') ->where('business_id', $business_id); $purchasesQuery->when($request->branch_id, function ($q) use ($request) { $q->where('branch_id', $request->branch_id); }); // Default to today $startDate = Carbon::today()->format('Y-m-d'); $endDate = Carbon::today()->format('Y-m-d'); if ($request->custom_days === 'yesterday') { $startDate = Carbon::yesterday()->format('Y-m-d'); $endDate = Carbon::yesterday()->format('Y-m-d'); } elseif ($request->custom_days === 'last_seven_days') { $startDate = Carbon::today()->subDays(6)->format('Y-m-d'); } elseif ($request->custom_days === 'last_thirty_days') { $startDate = Carbon::today()->subDays(29)->format('Y-m-d'); } elseif ($request->custom_days === 'current_month') { $startDate = Carbon::now()->startOfMonth()->format('Y-m-d'); $endDate = Carbon::now()->endOfMonth()->format('Y-m-d'); } elseif ($request->custom_days === 'last_month') { $startDate = Carbon::now()->subMonth()->startOfMonth()->format('Y-m-d'); $endDate = Carbon::now()->subMonth()->endOfMonth()->format('Y-m-d'); } elseif ($request->custom_days === 'current_year') { $startDate = Carbon::now()->startOfYear()->format('Y-m-d'); $endDate = Carbon::now()->endOfYear()->format('Y-m-d'); } elseif ($request->custom_days === 'custom_date' && $request->from_date && $request->to_date) { $startDate = Carbon::parse($request->from_date)->format('Y-m-d'); $endDate = Carbon::parse($request->to_date)->format('Y-m-d'); } $purchasesQuery->whereDate('purchaseDate', '>=', $startDate) ->whereDate('purchaseDate', '<=', $endDate); // Search Filter if ($request->filled('search')) { $purchasesQuery->where(function ($query) use ($request) { $query->where('paymentType', 'like', '%'.$request->search.'%') ->orWhere('invoiceNumber', 'like', '%'.$request->search.'%') ->orWhereHas('party', function ($q) use ($request) { $q->where('name', 'like', '%'.$request->search.'%'); }) ->orWhereHas('payment_type', function ($q) use ($request) { $q->where('name', 'like', '%'.$request->search.'%'); }) ->orWhereHas('branch', function ($q) use ($request) { $q->where('name', 'like', '%'.$request->search.'%'); }); }); } $perPage = $request->input('per_page', 20); $purchases = $purchasesQuery->latest()->paginate($perPage)->appends($request->query()); if ($request->ajax()) { return response()->json([ 'data' => view('business::purchases.datas', compact('purchases', 'purchasesWithReturns'))->render(), ]); } $branches = Branch::withTrashed()->where('business_id', $business_id)->latest()->get(); $payment_types = PaymentType::where('business_id', $business_id)->whereStatus(1)->latest()->get(); return view('business::purchases.index', compact('purchases', 'purchasesWithReturns', 'branches', 'payment_types')); } public function create() { $business_id = auth()->user()->business_id; $activeBranchId = auth()->user()->active_branch?->id; // Clears all cart items Cart::destroy(); $suppliers = Party::where('type', 'Supplier') ->where('business_id', $business_id) ->latest() ->get() ->transform(function ($supplier) use ($activeBranchId) { $supplier->due = $supplier->dueForBranch($activeBranchId); return $supplier; }); $products = Product::with(['category:id,categoryName', 'unit:id,unitName', 'brand:id,brandName']) ->withSum('stocks', 'productStock') ->where('business_id', $business_id) ->whereNot('product_type', 'combo') ->latest() ->get(); $cart_contents = Cart::content()->filter(fn ($item) => $item->options->type == 'purchase'); $categories = Category::where('business_id', $business_id)->latest()->get(); $brands = Brand::where('business_id', $business_id)->latest()->get(); $vats = Vat::where('business_id', $business_id)->whereStatus(1)->latest()->get(); $payment_types = PaymentType::where('business_id', $business_id)->whereStatus(1)->latest()->get(); $warehouses = Warehouse::select('id', 'name')->where('business_id', $business_id)->latest()->get(); $profit_option = Option::where('key', 'business-settings') ->where('value', 'LIKE', '%"business_id":' . $business_id . '%') ->get() ->firstWhere('value.business_id', $business_id) ->value['product_profit_option'] ?? ''; // Generate a unique invoice number $purchase_id = (Purchase::max('id') ?? 0) + 1; $invoice_no = 'P-'.str_pad($purchase_id, 5, '0', STR_PAD_LEFT); return view('business::purchases.create', compact('suppliers', 'products', 'cart_contents', 'invoice_no', 'categories', 'brands', 'vats', 'payment_types', 'warehouses', 'profit_option')); } public function store(Request $request) { $request->validate([ 'party_id' => 'required|exists:parties,id', 'invoiceNumber' => 'required|string', 'receive_amount' => 'nullable|numeric', 'vat_id' => 'nullable|exists:vats,id', 'discountAmount' => 'nullable|numeric', 'discount_type' => 'nullable|in:flat,percent', 'shipping_charge' => 'nullable|numeric', 'purchaseDate' => 'nullable|date', ]); $user = auth()->user(); $business = Business::findOrFail($user->business_id); $branch_id = $user->branch_id ?? $user->active_branch_id; // Check each cart item for batch duplication in Stock table $carts = Cart::content()->filter(fn ($item) => $item->options->type == 'purchase'); if ($carts->count() < 1) { return response()->json(['message' => __('Cart is empty. Add items first!')], 400); } DB::beginTransaction(); try { // Subtotal $subtotal = $carts->sum(fn ($cartItem) => (float) $cartItem->subtotal); // Discount (applied first on subtotal) $discountAmount = $request->discountAmount ?? 0; if ($request->discount_type === 'percent') { $discountAmount = ($subtotal * $discountAmount) / 100; } if ($discountAmount > $subtotal) { return response()->json(['message' => __('Discount cannot be more than subtotal!')], 400); } // Subtotal after discount (before VAT) $subtotalAfterDiscount = $subtotal - $discountAmount; // VAT (applied on subtotal after discount) $vat = Vat::find($request->vat_id); $vatAmount = $vat ? ($subtotalAfterDiscount * $vat->rate) / 100 : 0; // Charges and totals $shippingCharge = $request->shipping_charge ?? 0; $totalAmount = $subtotalAfterDiscount + $vatAmount + $shippingCharge; $receiveAmount = $request->receive_amount ?? 0; $changeAmount = max($receiveAmount - $totalAmount, 0); $dueAmount = max($totalAmount - $receiveAmount, 0); $paidAmount = $receiveAmount - $changeAmount; // Party due update if ($dueAmount > 0) { $party = Party::findOrFail($request->party_id); // Check party credit limit $newTotalDue = $party->due + $dueAmount; if ($party->credit_limit > 0 && $newTotalDue > $party->credit_limit) { return response()->json([ 'message' => __('Cannot create purchase. Party due will exceed credit limit!'), ], 400); } $party->update([ 'due' => $newTotalDue, ]); } // Business balance update updateBalance($paidAmount, 'decrement'); // Create Purchase $purchase = Purchase::create($request->except('discountAmount', 'discount_type', 'discount_percent', 'shipping_charge', 'payment_type_id') + [ 'user_id' => $user->id, 'vat_id' => $request->vat_id, 'vat_amount' => $vatAmount, 'business_id' => $user->business_id, 'discountAmount' => $discountAmount, 'discount_type' => $request->discount_type ?? 'flat', 'discount_percent' => $request->discount_type == 'percent' ? $request->discountAmount : 0, 'totalAmount' => $totalAmount, 'paidAmount' => $paidAmount, 'change_amount' => $changeAmount, 'dueAmount' => $dueAmount, 'shipping_charge' => $shippingCharge, 'purchaseDate' => $request->purchaseDate ? Carbon::parse($request->purchaseDate)->setTimeFromTimeString(now()->format('H:i:s')) : now(), 'isPaid' => $dueAmount > 0 ? 0 : 1, ]); // Insert Purchase Details and Create Stocks foreach ($carts as $cartItem) { $product = Product::findOrFail($cartItem->id); $batchNo = $cartItem->options['batch_no'] ?? null; $expireDate = $cartItem->options['expire_date'] ?? null; $variantName = $cartItem->options['variant_name'] ?? null; $existingStock = Stock::where([ 'business_id' => $user->business_id, 'batch_no' => $batchNo, 'product_id' => $cartItem->id, 'branch_id' => $branch_id, ]) ->when($variantName, fn ($q) => $q->where('variant_name', $variantName)) ->first(); $warehouseId = $cartItem->options['warehouse_id'] ?? ($existingStock->warehouse_id ?? null); $newSerials = $cartItem->options['serial_numbers'] ?? []; $existingSerials = $existingStock?->serial_numbers ?? []; // Only serials that don't already exist $uniqueNewSerials = array_diff($newSerials, $existingSerials); if (! empty($newSerials) && count($uniqueNewSerials) === 0) { return response()->json([ 'success' => false, 'message' => 'Serial already exists in stock', 'duplicates' => array_values(array_intersect($newSerials, $existingSerials)), ], 400); } $mergedSerials = array_values( array_unique(array_merge($existingSerials, $uniqueNewSerials)) ); // qty depends on serial $qtyToAdd = count($uniqueNewSerials) > 0 ? count($uniqueNewSerials) : $cartItem->qty; // price calculation $vatRate = 0; if ($product->vat_id) { $vatData = Vat::find($product->vat_id); $vatRate = $vatData?->rate ?? 0; } $purchasePrice = $cartItem->price; // convert inclusive to exclusive $exclusive_price = $vatRate > 0 ? round($purchasePrice / (1 + ($vatRate / 100)), 4) : $purchasePrice; $isSerialProduct = ! empty($newSerials); if ($isSerialProduct) { // For serial products, use cart sales_price directly $salesPrice = $cartItem->options['sales_price']; $profitPercent = $cartItem->options['profit_percent']; } else { $profitPercent = ($cartItem->options['profit_percent'] ?? 0); $profitOption = Option::where('key', 'business-settings') ->where('value', 'LIKE', '%"business_id":' . $business->id . '%') ->get() ->firstWhere('value.business_id', $business->id) ->value['product_profit_option'] ?? 'markup'; if ($profitOption === 'margin') { $salesPrice = $exclusive_price / (1 - ($profitPercent / 100)); } else { $salesPrice = $exclusive_price + ($exclusive_price * $profitPercent / 100); } if ($product->vat_type === 'inclusive' && $vatRate > 0) { $salesPrice = $salesPrice * (1 + ($vatRate / 100)); } $salesPrice = round($salesPrice, 2); } $stock = Stock::updateOrCreate( [ 'batch_no' => $batchNo, 'product_id' => $cartItem->id, 'variant_name' => $variantName, 'branch_id' => $branch_id, 'business_id' => $user->business_id, ], [ 'expire_date' => $expireDate, 'product_id' => $cartItem->id, 'warehouse_id' => $warehouseId, 'exclusive_price' => $exclusive_price, 'productPurchasePrice' => $purchasePrice, 'productSalePrice' => $salesPrice, 'profit_percent' => $profitPercent, 'productDealerPrice' => $cartItem->options['dealer_price'], 'serial_numbers' => ! empty($newSerials) ? $mergedSerials : null, 'productWholeSalePrice' => $cartItem->options['whole_sale_price'], 'productStock' => $cartItem->qty + ($existingStock->productStock ?? 0), ] ); // purchase detail $detail = PurchaseDetails::create([ 'stock_id' => $stock->id, 'purchase_id' => $purchase->id, 'product_id' => $cartItem->id, 'quantities' => $qtyToAdd, 'expire_date' => $expireDate, 'productPurchasePrice' => $purchasePrice, 'productSalePrice' => $salesPrice, 'profit_percent' => $profitPercent, 'productDealerPrice' => $cartItem->options['dealer_price'], 'productWholeSalePrice' => $cartItem->options['whole_sale_price'], 'serial_numbers' => ! empty($uniqueNewSerials) ? array_values($uniqueNewSerials) : null, ]); $vatAmount = PurchaseVatTransaction::storeVatTransactions($business, $detail, $product, $party ?? null); $detail->update([ 'price_without_tax' => number_format($purchasePrice - ($vatAmount / $cartItem->qty), 3, '.', ''), ]); } // MultiPaymentProcessed Event event(new MultiPaymentProcessed( $request->payments ?? [], $purchase->id, 'purchase', $request->receive_amount ?? 0, $request->party_id ?? null, )); // Clear cart foreach ($carts as $cartItem) { Cart::remove($cartItem->rowId); } sendNotifyToUser($purchase->id, route('business.purchases.index', ['id' => $purchase->id]), __('New Purchase created.'), $user->business_id); if (moduleCheck('MarketingAddon')) { $this->purchaseMessage($business, $party ?? null, $purchase); } $tax_invoice = moduleCheck('TaxInvoiceAddon') && invoice_setting($purchase->business_id) == 'standard_a4'; $invoice_route = $tax_invoice ? route('business.purchases.tax.invoice', $purchase->id) : route('business.purchases.invoice', $purchase->id); DB::commit(); return response()->json([ 'message' => __('Purchase created successfully.'), 'redirect' => route('business.purchases.index'), 'secondary_redirect_url' => $invoice_route, ]); } catch (\Exception $e) { DB::rollback(); return response()->json(['message' => $e->getMessage()], 500); } } public function edit($id) { // Clears all cart items Cart::destroy(); $business_id = auth()->user()->business_id; $activeBranchId = auth()->user()->active_branch?->id; $purchase = Purchase::with('details', 'details.product', 'details.stock:id,batch_no', 'details.product.unit', 'payment_type:id,name', 'transactions') ->where('business_id', $business_id) ->findOrFail($id); $suppliers = Party::where('type', 'Supplier') ->where('business_id', $business_id) ->latest() ->get() ->transform(function ($supplier) use ($activeBranchId) { $supplier->due = $supplier->dueForBranch($activeBranchId); return $supplier; }); $products = Product::with(['category:id,categoryName', 'unit:id,unitName', 'brand:id,brandName']) ->withSum('stocks', 'productStock') ->where('business_id', $business_id) ->whereNot('product_type', 'combo') ->latest() ->get(); $categories = Category::where('business_id', $business_id)->latest()->get(); $brands = Brand::where('business_id', $business_id)->latest()->get(); $vats = Vat::where('business_id', $business_id)->whereStatus(1)->latest()->get(); $payment_types = PaymentType::where('business_id', $business_id)->whereStatus(1)->latest()->get(); $warehouses = Warehouse::select('id', 'name')->where('business_id', $business_id)->latest()->get(); $profit_option = Option::where('key', 'business-settings') ->where('value', 'LIKE', '%"business_id":' . $business_id . '%') ->get() ->firstWhere('value.business_id', $business_id) ->value['product_profit_option'] ?? ''; // Add purchase details to the cart foreach ($purchase->details as $detail) { $stock = Stock::where('id', $detail->stock_id)->first(); // Add to cart Cart::add([ 'id' => $detail->product_id, 'name' => $detail->product->productName ?? '', 'qty' => $detail->quantities, 'price' => $detail->productPurchasePrice, 'options' => [ 'type' => 'purchase', 'product_code' => $detail->product->productCode ?? '', 'product_unit_id' => $detail->product->unit_id ?? null, 'product_unit_name' => $detail->product->unit->unitName ?? '', 'product_image' => $detail->product->productPicture ?? '', 'sales_price' => $detail->productSalePrice, 'profit_percent' => $detail->profit_percent, 'whole_sale_price' => $detail->productWholeSalePrice, 'dealer_price' => $detail->productDealerPrice, 'batch_no' => $detail->stock->batch_no ?? null, 'expire_date' => $detail->expire_date, 'variant_name' => $stock->variant_name ?? null, 'warehouse_id' => $stock->warehouse_id ?? null, 'price_without_tax' => $detail->price_without_tax ?? 0, 'serial_numbers' => $detail->serial_numbers ?? null, 'has_serial' => $detail->product->has_serial ?? null, ], ]); } $cart_contents = Cart::content()->filter(fn ($item) => $item->options->type == 'purchase'); return view('business::purchases.edit', compact('purchase', 'suppliers', 'products', 'cart_contents', 'categories', 'brands', 'vats', 'payment_types', 'warehouses', 'profit_option')); } public function update(Request $request, $id) { $request->validate([ 'party_id' => 'required|exists:parties,id', 'invoiceNumber' => 'required|string', 'receive_amount' => 'nullable|numeric', 'vat_id' => 'nullable|exists:vats,id', 'discountAmount' => 'nullable|numeric', 'discount_type' => 'nullable|in:flat,percent', 'shipping_charge' => 'nullable|numeric', 'purchaseDate' => 'nullable|date', ]); $user = auth()->user(); $business = Business::findOrFail($user->business_id); $carts = Cart::content()->filter(fn ($item) => $item->options->type === 'purchase'); if ($carts->count() < 1) { return response()->json(['message' => __('Cart is empty. Add items first!')], 400); } DB::beginTransaction(); try { $purchase = Purchase::with('details')->findOrFail($id); // Calculate amounts $subtotal = $carts->sum(fn ($cartItem) => (float) $cartItem->subtotal); // Discount (applied first on subtotal) $discountAmount = $request->discountAmount ?? 0; if ($request->discount_type == 'percent') { $discountAmount = ($subtotal * $discountAmount) / 100; } if ($discountAmount > $subtotal) { return response()->json([ 'message' => __('Discount cannot be more than subtotal!'), ], 400); } // Subtotal after discount (before VAT) $subtotalAfterDiscount = $subtotal - $discountAmount; // VAT (applied on subtotal after discount) $vat = Vat::find($request->vat_id); $vatAmount = 0; if ($vat) { $vatAmount = ($subtotalAfterDiscount * $vat->rate) / 100; } // Shipping Charge $shippingCharge = $request->shipping_charge ?? 0; // Total Amount $totalAmount = $subtotalAfterDiscount + $vatAmount + $shippingCharge; // Receive, Change, Due Amount Calculation $receiveAmount = $request->receive_amount ?? 0; $changeAmount = $receiveAmount > $totalAmount ? $receiveAmount - $totalAmount : 0; $dueAmount = max($totalAmount - $receiveAmount, 0); $paidAmount = $receiveAmount - $changeAmount; if ($purchase->dueAmount || $dueAmount) { $party = Party::findOrFail($request->party_id); // If same party, adjust old due and add new one $newDue = $request->party_id == $purchase->party_id ? ($party->due - $purchase->dueAmount) + $dueAmount : $party->due + $dueAmount; // Check credit limit if ($party->credit_limit > 0 && $newDue > $party->credit_limit) { return response()->json(['message' => __('Party credit limit exceeded!')], 400); } $party->update(['due' => $newDue]); // If changed to a new party, reduce previous party’s due if ($request->party_id != $purchase->party_id) { $prev_party = Party::findOrFail($purchase->party_id); $prev_party->update(['due' => $prev_party->due - $purchase->dueAmount]); } } // branch/business balance updateBalance($purchase->paidAmount - $paidAmount, 'decrement'); // Update purchase details $purchase->update($request->except('discountAmount', 'discount_type', 'discount_percent', 'shipping_charge') + [ 'business_id' => $user->business_id, 'user_id' => $user->id, 'vat_id' => $request->vat_id, 'vat_amount' => $vatAmount, 'discountAmount' => $discountAmount, 'totalAmount' => $totalAmount, 'paidAmount' => $paidAmount, 'change_amount' => $changeAmount, 'dueAmount' => $dueAmount, 'isPaid' => $dueAmount > 0 ? 0 : 1, 'shipping_charge' => $shippingCharge, 'payment_type_id' => $request->payment_type_id, 'discount_type' => $request->discount_type ?? 'flat', 'discount_percent' => $request->discount_type == 'percent' ? $request->discountAmount : 0, 'purchaseDate' => $request->purchaseDate ? Carbon::parse($request->purchaseDate)->setTimeFromTimeString(now()->format('H:i:s')) : now(), ]); // Revert previous stock changes foreach ($purchase->details as $detail) { $stock = Stock::find($detail->stock_id); if ($stock) { if (! empty($detail->serial_numbers)) { $stock->serial_numbers = array_values(array_diff($stock->serial_numbers, $detail->serial_numbers)); $stock->productStock = count($stock->serial_numbers); } else { $stock->productStock -= $detail->quantities; } $stock->save(); } } // Store old purchase details before deleting $oldDetails = $purchase->details->keyBy('product_id'); // Delete existing purchase details $prev_details_ids = $oldDetails->pluck('id')->toArray(); PurchaseDetails::where('purchase_id', $purchase->id)->delete(); // remove previous vat transactions VatTransaction::where('vatable_id', $prev_details_ids) ->where('vatable_type', PurchaseDetails::class) ->delete(); // Insert updated purchase details and adjust stock foreach ($carts as $cartItem) { $product = Product::findOrFail($cartItem->id); $batch_no = $cartItem->options['batch_no'] ?? null; $expire_date = $cartItem->options['expire_date'] ?? null; $variantName = $cartItem->options['variant_name'] ?? null; $dealer_price = $cartItem->options['dealer_price'] ?? 0.0; $whole_sale_price = $cartItem->options['whole_sale_price'] ?? 0.0; $existingStock = Stock::where(['batch_no' => $batch_no, 'product_id' => $cartItem->id]) ->when($variantName, fn ($q) => $q->where('variant_name', $variantName)) ->first(); $warehouseId = $cartItem->options['warehouse_id'] ?? ($existingStock->warehouse_id ?? null); // serial logic $newSerials = $cartItem->options['serial_numbers'] ?? []; $purchaseSerials = ! empty($newSerials) ? array_values($newSerials) : null; $existingSerials = $existingStock?->serial_numbers ?? []; // get previous purchase serials $oldPurchaseSerials = $oldDetails[$cartItem->id]->serial_numbers ?? []; $remainingSerials = array_diff($existingSerials, $oldPurchaseSerials); $mergedSerials = ! empty($newSerials) ? array_values(array_unique(array_merge($remainingSerials, $newSerials))) : array_values($remainingSerials); if (! empty($newSerials)) { // Serial Product $stockQty = count($mergedSerials); $purchaseQty = count($newSerials); } else { // Non Serial Product $stockQty = ($existingStock->productStock ?? 0) + $cartItem->qty; $purchaseQty = $cartItem->qty; } // Price Calculation $purchasePrice = $cartItem->price; // Exclusive Price $exclusive_price = $purchasePrice; $vatRate = 0; if ($product->vat_id) { $vat = Vat::find($product->vat_id); $vatRate = $vat?->rate ?? 0; if ($vatRate > 0) { $exclusive_price = round($purchasePrice / (1 + ($vatRate / 100)), 3); } } $isSerialProduct = ! empty($newSerials); if ($isSerialProduct) { // For serial products, use cart sales_price directly $salesPrice = $cartItem->options['sales_price'] ?? $purchasePrice; $profitPercent = 0; } else { $profitPercent = $cartItem->options['profit_percent'] ?? 0; $profitOption = Option::where('key', 'business-settings') ->where('value', 'LIKE', '%"business_id":' . $business->id . '%') ->get() ->firstWhere('value.business_id', $business->id) ->value['product_profit_option'] ?? 'markup'; if ($profitOption === 'margin') { $salesPrice = $exclusive_price / (1 - ($profitPercent / 100)); } else { $salesPrice = $exclusive_price + ($exclusive_price * $profitPercent / 100); } if ($product->vat_type === 'inclusive' && $vatRate > 0) { $salesPrice = $salesPrice * (1 + ($vatRate / 100)); } $salesPrice = round($salesPrice, 3); } // update or create stock $stock = Stock::updateOrCreate( [ 'batch_no' => $batch_no, 'product_id' => $cartItem->id, 'variant_name' => $variantName, 'business_id' => $user->business_id, ], [ 'product_id' => $cartItem->id, 'expire_date' => $expire_date, 'productDealerPrice' => $dealer_price, 'productPurchasePrice' => $purchasePrice, 'exclusive_price' => $exclusive_price, 'profit_percent' => $profitPercent, 'productSalePrice' => $salesPrice, 'productWholeSalePrice' => $whole_sale_price, 'productStock' => $stockQty, 'warehouse_id' => $warehouseId, 'serial_numbers' => $mergedSerials, ] ); $detail = PurchaseDetails::create([ 'purchase_id' => $purchase->id, 'product_id' => $cartItem->id, 'quantities' => $purchaseQty, 'stock_id' => $stock->id, 'expire_date' => $expire_date, 'productPurchasePrice' => $purchasePrice, 'profit_percent' => $profitPercent, 'productSalePrice' => $salesPrice, 'serial_numbers' => $purchaseSerials, 'productDealerPrice' => $dealer_price, 'productWholeSalePrice' => $whole_sale_price, ]); $vatAmount = PurchaseVatTransaction::storeVatTransactions($business, $detail, $product, $party ?? null); $detail->update([ 'price_without_tax' => number_format($purchasePrice - ($vatAmount / $purchaseQty), 3, '.', ''), ]); } // Multiple Payment Process $oldTransactions = Transaction::where('business_id', $user->business_id) ->where('reference_id', $purchase->id) ->where('platform', 'purchase') ->get(); // Revert old transactions foreach ($oldTransactions as $old) { switch ($old->transaction_type) { case 'bank_payment': $paymentType = PaymentType::find($old->payment_type_id); if ($paymentType) { $paymentType->increment('balance', $old->amount); } break; case 'wallet_payment': if ($request->party_id) { $party = Party::find($request->party_id); if ($party) { $party->decrement('wallet', $old->amount); } } break; case 'cash_payment': case 'cheque_payment': // nothing to revert break; } } // Delete old transactions Transaction::where('business_id', $user->business_id) ->where('reference_id', $purchase->id) ->where('platform', 'purchase') ->delete(); event(new MultiPaymentProcessed( $request->payments ?? [], $purchase->id, 'purchase', $request->receive_amount ?? 0, $request->party_id ?? null, )); // Clear the cart foreach ($carts as $cartItem) { Cart::remove($cartItem->rowId); } sendNotifyToUser($purchase->id, route('business.purchases.index', ['id' => $purchase->id]), __('Purchase has been updated.'), $user->business_id); $tax_invoice = moduleCheck('TaxInvoiceAddon') && invoice_setting($purchase->business_id) == 'standard_a4'; $invoice_route = $tax_invoice ? route('business.purchases.tax.invoice', $purchase->id) : route('business.purchases.invoice', $purchase->id); DB::commit(); return response()->json([ 'message' => __('Purchase updated successfully.'), 'redirect' => route('business.purchases.index'), 'secondary_redirect_url' => $invoice_route, ]); } catch (\Exception $e) { DB::rollback(); return response()->json(['message' => $e->getMessage()], 500); } } public function destroy($id) { DB::beginTransaction(); try { $purchase = Purchase::with('details')->findOrFail($id); foreach ($purchase->details as $detail) { VatTransaction::where('vatable_id', $detail->id) ->where('vatable_type', get_class($detail)) ->delete(); Stock::where('id', $detail->stock_id)->decrement('productStock', $detail->quantities); } if ($purchase->party_id) { $party = Party::findOrFail($purchase->party_id); $party->update([ 'due' => $party->due - $purchase->dueAmount, ]); } updateBalance($purchase->paidAmount, 'increment'); sendNotifyToUser($purchase->id, route('business.purchases.index', ['id' => $purchase->id]), __('Purchase has been deleted.'), $purchase->business_id); $purchase->delete(); // Clears all cart items Cart::destroy(); DB::commit(); return response()->json([ 'message' => __('Purchase deleted successfully.'), 'redirect' => route('business.purchases.index'), ]); } catch (\Exception $e) { DB::rollback(); return response()->json(['message' => $e->getMessage()], 404); } } public function deleteAll(Request $request) { DB::beginTransaction(); try { $purchases = Purchase::where('business_id', auth()->user()->business_id)->whereIn('id', $request->ids)->get(); foreach ($purchases as $purchase) { foreach ($purchase->details as $detail) { VatTransaction::where('vatable_id', $detail->id) ->where('vatable_type', get_class($detail)) ->delete(); Stock::where('id', $detail->stock_id)->decrement('productStock', $detail->quantities); } if ($purchase->party_id) { $party = Party::findOrFail($purchase->party_id); $party->update([ 'due' => $party->due - $purchase->dueAmount, ]); } updateBalance($purchase->paidAmount, 'increment'); sendNotifyToUser($purchase->id, route('business.purchases.index', ['id' => $purchase->id]), __('Purchases has been deleted.'), $purchase->business_id); $purchase->delete(); } // Clears all cart items Cart::destroy(); DB::commit(); return response()->json([ 'message' => __('Selected purchases deleted successfully.'), 'redirect' => route('business.purchases.index'), ]); } catch (\Exception $e) { DB::rollback(); return response()->json(['message' => $e->getMessage()], 404); } } public function acnooFilter(Request $request) { $business_id = auth()->user()->business_id; $purchasesWithReturns = PurchaseReturn::where('business_id', $business_id) ->pluck('purchase_id') ->toArray(); $purchasesQuery = Purchase::with('user:id,name', 'party:id,name,email,phone,type', 'payment_type:id,name', 'branch:id,name') ->where('business_id', $business_id); $purchasesQuery->when($request->branch_id, function ($q) use ($request) { $q->where('branch_id', $request->branch_id); }); // Default to today $startDate = Carbon::today()->format('Y-m-d'); $endDate = Carbon::today()->format('Y-m-d'); if ($request->custom_days === 'yesterday') { $startDate = Carbon::yesterday()->format('Y-m-d'); $endDate = Carbon::yesterday()->format('Y-m-d'); } elseif ($request->custom_days === 'last_seven_days') { $startDate = Carbon::today()->subDays(6)->format('Y-m-d'); } elseif ($request->custom_days === 'last_thirty_days') { $startDate = Carbon::today()->subDays(29)->format('Y-m-d'); } elseif ($request->custom_days === 'current_month') { $startDate = Carbon::now()->startOfMonth()->format('Y-m-d'); $endDate = Carbon::now()->endOfMonth()->format('Y-m-d'); } elseif ($request->custom_days === 'last_month') { $startDate = Carbon::now()->subMonth()->startOfMonth()->format('Y-m-d'); $endDate = Carbon::now()->subMonth()->endOfMonth()->format('Y-m-d'); } elseif ($request->custom_days === 'current_year') { $startDate = Carbon::now()->startOfYear()->format('Y-m-d'); $endDate = Carbon::now()->endOfYear()->format('Y-m-d'); } elseif ($request->custom_days === 'custom_date' && $request->from_date && $request->to_date) { $startDate = Carbon::parse($request->from_date)->format('Y-m-d'); $endDate = Carbon::parse($request->to_date)->format('Y-m-d'); } $purchasesQuery->whereDate('purchaseDate', '>=', $startDate) ->whereDate('purchaseDate', '<=', $endDate); // Search Filter if ($request->filled('search')) { $purchasesQuery->where(function ($query) use ($request) { $query->where('paymentType', 'like', '%'.$request->search.'%') ->orWhere('invoiceNumber', 'like', '%'.$request->search.'%') ->orWhereHas('party', function ($q) use ($request) { $q->where('name', 'like', '%'.$request->search.'%'); }) ->orWhereHas('payment_type', function ($q) use ($request) { $q->where('name', 'like', '%'.$request->search.'%'); }) ->orWhereHas('branch', function ($q) use ($request) { $q->where('name', 'like', '%'.$request->search.'%'); }); }); } $perPage = $request->input('per_page', 10); $purchases = $purchasesQuery->latest()->paginate($perPage); if ($request->ajax()) { return response()->json([ 'data' => view('business::purchases.datas', compact('purchases', 'purchasesWithReturns'))->render(), ]); } return redirect(url()->previous()); } public function productFilter(Request $request) { $business_id = auth()->user()->business_id; $products = Product::where('business_id', $business_id) ->when($request->search, function ($query) use ($request) { $query->where(function ($q) use ($request) { $q->where('productName', 'like', '%'.$request->search.'%') ->orWhere('productCode', 'like', '%'.$request->search.'%'); }); }) ->when($request->category_id, function ($query) use ($request) { $query->where('category_id', $request->category_id); }) ->when($request->brand_id, function ($query) use ($request) { $query->where('brand_id', $request->brand_id); }) ->withSum('stocks as total_stock', 'productStock') ->latest() ->get(); // Query categories for search options $categories = Category::where('business_id', $business_id) ->when($request->search, function ($query) use ($request) { $query->where('categoryName', 'like', '%'.$request->search.'%'); }) ->get(); // Query brands for search options $brands = Brand::where('business_id', $business_id) ->when($request->search, function ($query) use ($request) { $query->where('brandName', 'like', '%'.$request->search.'%'); }) ->get(); $total_products = $products->count(); if ($request->ajax()) { return response()->json([ 'total_products' => $total_products, 'product_id' => $total_products == 1 ? $products->first()->id : null, 'data' => view('business::purchases.product-list', compact('products'))->render(), 'categories' => view('business::purchases.category-list', compact('categories'))->render(), 'brands' => view('business::purchases.brand-list', compact('brands'))->render(), ]); } return redirect(url()->previous()); } // Category search Filter public function categoryFilter(Request $request) { $search = $request->search; $categories = Category::where('business_id', auth()->user()->business_id) ->when($search, function ($query) use ($search) { $query->where('categoryName', 'like', '%'.$search.'%'); }) ->get(); return response()->json([ 'categories' => view('business::purchases.category-list', compact('categories'))->render(), ]); } // Brand search Filter public function brandFilter(Request $request) { $search = $request->search; $brands = Brand::where('business_id', auth()->user()->business_id) ->when($search, function ($query) use ($search) { $query->where('brandName', 'like', '%'.$search.'%'); }) ->get(); return response()->json([ 'brands' => view('business::purchases.brand-list', compact('brands'))->render(), ]); } public function getInvoice($purchase_id, PurchaseInvoiceService $service) { $data = $service->getInvoiceData($purchase_id); return view('business::purchases.invoice', $data); } public function getTaxInvoice($purchase_id, PurchaseTaxInvoiceService $service) { $data = $service->getInvoiceData($purchase_id); return view('taxinvoiceaddon::purchases.tax', $data); } public function showPurchaseCart() { $cart_contents = Cart::content()->filter(fn ($item) => $item->options->type == 'purchase'); return view('business::purchases.cart-list', compact('cart_contents')); } public function getCartData() { $cart_contents = Cart::content()->filter(fn ($item) => $item->options->type == 'purchase'); $data['sub_total'] = 0; foreach ($cart_contents as $cart) { $data['sub_total'] += $cart->price; } $data['sub_total'] = currency_format($data['sub_total'], currency: business_currency()); return response()->json($data); } public function generatePDF($purchase_id) { $purchase = Purchase::where('business_id', auth()->user()->business_id) ->with( 'user:id,name,role', 'party:id,name,phone,address', 'business:id,phoneNumber,companyName,vat_name,vat_no,address,email,meta', 'details:id,productPurchasePrice,quantities,product_id,purchase_id,stock_id', 'details.stock:id,batch_no', 'details.product:id,productName', 'payment_type:id,name', 'transactions:id,reference_id,transaction_type,payment_type_id', 'transactions.paymentType:id,name' ) ->findOrFail($purchase_id); /** * Transaction types (same helper used in Sale) */ $transactionTypes = transaction_types($purchase->transactions); /** * Purchase returns */ $purchase_returns = PurchaseReturn::with( 'purchase:id,party_id,isPaid,totalAmount,dueAmount,paidAmount,invoiceNumber', 'purchase.party:id,name', 'details', 'details.purchaseDetail:id,productPurchasePrice,product_id,stock_id', 'details.purchaseDetail.product:id,productName', 'details.purchaseDetail.stock:id,batch_no' ) ->where('business_id', auth()->user()->business_id) ->where('purchase_id', $purchase_id) ->latest() ->get(); /** * Return transaction type (same logic as Sale) */ $returnTransaction = $purchase_returns->first()?->transactions->first(); $returnTransactionType = ''; if ($returnTransaction) { $returnTransactionType = $returnTransaction->transaction_type === 'bank_payment' ? ($returnTransaction->paymentType?->name) : ucfirst(explode('_', $returnTransaction->transaction_type)[0]); } /** * Merge return qty into purchase details (same as Sale) */ $purchase->details = $purchase->details->map(function ($detail) use ($purchase_returns) { $return_qty_sum = $purchase_returns->flatMap(function ($return) use ($detail) { return $return->details ->where('purchaseDetail.id', $detail->id) ->pluck('return_qty'); })->sum(); $detail->quantities += $return_qty_sum; return $detail; }); /** * Discount calculation from returned items (same pattern as Sale) */ $total_discount = 0; $product_discounts = []; foreach ($purchase_returns as $return) { foreach ($return->details as $detail) { if (! isset($product_discounts[$detail->purchase_detail_id])) { $product_discounts[$detail->purchase_detail_id] = [ 'return_qty' => 0, 'return_amount' => 0, 'price' => $detail->purchaseDetail->productPurchasePrice, ]; } $product_discounts[$detail->purchase_detail_id]['return_qty'] += $detail->return_qty; $product_discounts[$detail->purchase_detail_id]['return_amount'] += $detail->return_amount; } } foreach ($product_discounts as $data) { $product_price = $data['price'] * $data['return_qty']; $total_discount += ($product_price - $data['return_amount']); } /** * Bank / Payment info */ $bank_detail = PaymentType::where('business_id', auth()->user()->business_id) ->latest() ->first(); /** * CENTRALIZED PDF RENDER (same as Sale) */ return PdfService::render( 'business::purchases.pdf', compact( 'purchase', 'purchase_returns', 'total_discount', 'transactionTypes', 'returnTransactionType', 'bank_detail' ), 'purchase-invoice.pdf' ); } public function generateTaxPDF($purchase_id) { $purchase = Purchase::where('business_id', auth()->user()->business_id) ->with( 'user:id,name,role', 'party:id,name,phone,address,email', 'business:id,phoneNumber,companyName,vat_name,vat_no,address,email,meta', 'details:id,price_without_tax,productPurchasePrice,quantities,product_id,purchase_id,serial_numbers', 'details.stock:id,batch_no', 'details.product:id,vat_id,productName,has_serial', 'payment_type:id,name' ) ->findOrFail($purchase_id); $business_id = auth()->check() ? auth()->user()->business_id : null; /** * Transaction types (same helper used in Sale) */ $detail_ids = $purchase->details->pluck('id'); $tax_items = VatTransaction::selectRaw('vat_id, vat_rate, SUM(vat_amount) as vat_amount') ->with('vat:id,name') ->whereIn('vatable_id', $detail_ids) ->where('vatable_type', PurchaseDetails::class) ->groupBy('vat_id', 'vat_rate') ->get(); $vatRowMap = VatTransaction::whereIn('vatable_id', $detail_ids) ->where('vatable_type', PurchaseDetails::class) ->selectRaw('vatable_id, vat_id, SUM(vat_amount) as total') ->groupBy('vatable_id', 'vat_id') ->get() ->groupBy('vatable_id') ->map(function ($items) { return $items->pluck('total', 'vat_id')->toArray(); }) ->toArray(); $transactionTypes = transaction_types($purchase->transactions); /** * Purchase returns */ $purchase_returns = PurchaseReturn::with( 'purchase:id,party_id,isPaid,totalAmount,dueAmount,paidAmount,invoiceNumber', 'purchase.party:id,name,email', 'details', 'details.purchaseDetail.product:id,productName,has_serial', 'details.purchaseDetail.stock:id,batch_no' ) ->where('business_id', auth()->user()->business_id) ->where('purchase_id', $purchase_id) ->latest() ->get(); // collect return detail ids $return_detail_ids = $purchase_returns->flatMap(function ($return) { return $return->details->pluck('id'); }); // get VAT for purchase return details $returnVatRowMap = VatTransaction::whereIn('vatable_id', $return_detail_ids) ->where('vatable_type', PurchaseReturnDetail::class) ->selectRaw('vatable_id, vat_id, SUM(vat_amount) as total') ->groupBy('vatable_id', 'vat_id') ->get() ->groupBy('vatable_id') ->map(function ($items) { return $items->pluck('total', 'vat_id')->toArray(); }) ->toArray(); /** * Return transaction type (same logic as Sale) */ $returnTransaction = $purchase_returns->first()?->transactions->first(); $returnTransactionType = ''; if ($returnTransaction) { $returnTransactionType = $returnTransaction->transaction_type === 'bank_payment' ? ($returnTransaction->paymentType?->name) : ucfirst(explode('_', $returnTransaction->transaction_type)[0]); } /** * Merge return qty into purchase details (same as Sale) */ $purchase->details = $purchase->details->map(function ($detail) use ($purchase_returns) { $return_qty_sum = $purchase_returns->flatMap(function ($return) use ($detail) { return $return->details ->where('purchaseDetail.id', $detail->id) ->pluck('return_qty'); })->sum(); $detail->quantities += $return_qty_sum; return $detail; }); /** * Discount calculation from returned items (same pattern as Sale) */ $total_discount = 0; $product_discounts = []; foreach ($purchase_returns as $return) { foreach ($return->details as $detail) { if (! isset($product_discounts[$detail->purchase_detail_id])) { $product_discounts[$detail->purchase_detail_id] = [ 'return_qty' => 0, 'return_amount' => 0, 'price' => $detail->purchaseDetail->productPurchasePrice, ]; } $product_discounts[$detail->purchase_detail_id]['return_qty'] += $detail->return_qty; $product_discounts[$detail->purchase_detail_id]['return_amount'] += $detail->return_amount; } } foreach ($product_discounts as $data) { $product_price = $data['price'] * $data['return_qty']; $total_discount += ($product_price - $data['return_amount']); } /** * Bank / Payment info */ $bank_detail = PaymentType::where('business_id', auth()->user()->business_id) ->latest() ->first(); /** * CENTRALIZED PDF RENDER (same as Sale) */ return PdfService::render( 'taxinvoiceaddon::purchases.pdf', compact( 'purchase', 'purchase_returns', 'total_discount', 'transactionTypes', 'returnTransactionType', 'bank_detail', 'tax_items', 'business_id', 'vatRowMap', 'returnVatRowMap' ), 'purchase-tax-invoice.pdf' ); } public function sendMail($purchase_id) { $purchase = Purchase::with('user:id,name,role', 'party:id,name,phone,address', 'business:id,phoneNumber,companyName,vat_name,vat_no,address,email,meta', 'details:id,productPurchasePrice,quantities,product_id,purchase_id', 'details.stock:id,batch_no', 'details.product:id,productName', 'payment_type:id,name') ->findOrFail($purchase_id); $transactionTypes = $purchase->transactions ->map(function ($transaction) { if ($transaction->transaction_type === 'bank_payment' && ! empty($transaction->paymentType?->name)) { return $transaction->paymentType->name; } return $transaction->transaction_type ? ucfirst(explode('_', $transaction->transaction_type)[0]) : ''; }) ->unique() ->implode(', '); $purchase_returns = PurchaseReturn::with('purchase:id,party_id,isPaid,totalAmount,dueAmount,paidAmount,invoiceNumber', 'purchase.party:id,name', 'details', 'details.purchaseDetail.product:id,productName', 'details.purchaseDetail.stock:id,batch_no') ->where('business_id', auth()->user()->business_id) ->where('purchase_id', $purchase_id) ->latest() ->get(); $returnTransaction = $purchase_returns->first()?->transactions->first(); $returnTransactionType = ''; if ($returnTransaction) { $returnTransactionType = $returnTransaction->transaction_type === 'bank_payment' ? ($returnTransaction->paymentType?->name) : ucfirst(explode('_', $returnTransaction->transaction_type)[0]); } // sum of return_qty $purchase->details = $purchase->details->map(function ($detail) use ($purchase_returns) { $return_qty_sum = $purchase_returns->flatMap(function ($return) use ($detail) { return $return->details->where('purchaseDetail.id', $detail->id)->pluck('return_qty'); })->sum(); $detail->quantities = $detail->quantities + $return_qty_sum; return $detail; }); // Calculate total discount based on return quantities and amounts $total_discount = 0; $product_discounts = []; foreach ($purchase_returns as $return) { foreach ($return->details as $detail) { // Initialize discount tracking for the first occurrence of each purchase_detail_id if (! isset($product_discounts[$detail->purchase_detail_id])) { $product_discounts[$detail->purchase_detail_id] = [ 'return_qty' => 0, 'return_amount' => 0, 'price' => $detail->purchaseDetail->productPurchasePrice, ]; } // Accumulate return quantities and return amounts $product_discounts[$detail->purchase_detail_id]['return_qty'] += $detail->return_qty; $product_discounts[$detail->purchase_detail_id]['return_amount'] += $detail->return_amount; } } // Calculate the total discount for each returned product foreach ($product_discounts as $data) { $product_price = $data['price'] * $data['return_qty']; $discount = $product_price - $data['return_amount']; $total_discount += $discount; } $bank_detail = PaymentType::where('business_id', auth()->user()->business_id)->where('show_in_invoice', 1)->latest()->first(); $pdf = Pdf::loadView('business::purchases.pdf', compact('purchase', 'purchase_returns', 'total_discount', 'transactionTypes', 'bank_detail', 'returnTransactionType')); // Send email with PDF attachment Mail::raw('Please find attached your Purchase invoice.', function ($message) use ($pdf) { $message->to(auth()->user()->email) ->subject('Purchase Invoice') ->attachData($pdf->output(), 'purchase-invoice.pdf', [ 'mime' => 'application/pdf', ]); }); return response()->json([ 'message' => __('Email Sent Successfully.'), 'redirect' => route('business.purchases.index'), ]); } public function sendTaxMail($purchase_id) { $purchase = Purchase::with('user:id,name,role', 'party:id,name,phone,address,email', 'business:id,phoneNumber,companyName,vat_name,vat_no,address,email,meta', 'details:id,price_without_tax,productPurchasePrice,quantities,product_id,purchase_id,serial_numbers', 'details.stock:id,batch_no', 'details.product:id,vat_id,productName,has_serial', 'payment_type:id,name') ->findOrFail($purchase_id); $business_id = auth()->check() ? auth()->user()->business_id : null; $detail_ids = $purchase->details->pluck('id'); $tax_items = VatTransaction::selectRaw('vat_id, vat_rate, SUM(vat_amount) as vat_amount') ->with('vat:id,name') ->whereIn('vatable_id', $detail_ids) ->where('vatable_type', PurchaseDetails::class) ->groupBy('vat_id', 'vat_rate') ->get(); $vatRowMap = VatTransaction::whereIn('vatable_id', $detail_ids) ->where('vatable_type', PurchaseDetails::class) ->selectRaw('vatable_id, vat_id, SUM(vat_amount) as total') ->groupBy('vatable_id', 'vat_id') ->get() ->groupBy('vatable_id') ->map(function ($items) { return $items->pluck('total', 'vat_id')->toArray(); }) ->toArray(); $transactionTypes = $purchase->transactions ->map(function ($transaction) { if ($transaction->transaction_type === 'bank_payment' && ! empty($transaction->paymentType?->name)) { return $transaction->paymentType->name; } return $transaction->transaction_type ? ucfirst(explode('_', $transaction->transaction_type)[0]) : ''; }) ->unique() ->implode(', '); $purchase_returns = PurchaseReturn::with('purchase:id,party_id,isPaid,totalAmount,dueAmount,paidAmount,invoiceNumber', 'purchase.party:id,name,email', 'details', 'details.purchaseDetail.product:id,productName,has_serial', 'details.purchaseDetail.stock:id,batch_no') ->where('business_id', auth()->user()->business_id) ->where('purchase_id', $purchase_id) ->latest() ->get(); $return_detail_ids = $purchase_returns->flatMap(function ($return) { return $return->details->pluck('id'); }); // get VAT for purchase return details $returnVatRowMap = VatTransaction::whereIn('vatable_id', $return_detail_ids) ->where('vatable_type', PurchaseReturnDetail::class) ->selectRaw('vatable_id, vat_id, SUM(vat_amount) as total') ->groupBy('vatable_id', 'vat_id') ->get() ->groupBy('vatable_id') ->map(function ($items) { return $items->pluck('total', 'vat_id')->toArray(); }) ->toArray(); $returnTransaction = $purchase_returns->first()?->transactions->first(); $returnTransactionType = ''; if ($returnTransaction) { $returnTransactionType = $returnTransaction->transaction_type === 'bank_payment' ? ($returnTransaction->paymentType?->name) : ucfirst(explode('_', $returnTransaction->transaction_type)[0]); } // sum of return_qty $purchase->details = $purchase->details->map(function ($detail) use ($purchase_returns) { $return_qty_sum = $purchase_returns->flatMap(function ($return) use ($detail) { return $return->details->where('purchaseDetail.id', $detail->id)->pluck('return_qty'); })->sum(); $detail->quantities = $detail->quantities + $return_qty_sum; return $detail; }); // Calculate total discount based on return quantities and amounts $total_discount = 0; $product_discounts = []; foreach ($purchase_returns as $return) { foreach ($return->details as $detail) { // Initialize discount tracking for the first occurrence of each purchase_detail_id if (! isset($product_discounts[$detail->purchase_detail_id])) { $product_discounts[$detail->purchase_detail_id] = [ 'return_qty' => 0, 'return_amount' => 0, 'price' => $detail->purchaseDetail->productPurchasePrice, ]; } // Accumulate return quantities and return amounts $product_discounts[$detail->purchase_detail_id]['return_qty'] += $detail->return_qty; $product_discounts[$detail->purchase_detail_id]['return_amount'] += $detail->return_amount; } } // Calculate the total discount for each returned product foreach ($product_discounts as $data) { $product_price = $data['price'] * $data['return_qty']; $discount = $product_price - $data['return_amount']; $total_discount += $discount; } $bank_detail = PaymentType::where('business_id', auth()->user()->business_id)->where('show_in_invoice', 1)->latest()->first(); $pdf = Pdf::loadView('taxinvoiceaddon::purchases.pdf', compact('purchase', 'purchase_returns', 'total_discount', 'transactionTypes', 'bank_detail', 'returnTransactionType', 'tax_items', 'business_id', 'vatRowMap', 'returnVatRowMap')); // Send email with PDF attachment Mail::raw('Please find attached your Purchase invoice.', function ($message) use ($pdf) { $message->to(auth()->user()->email) ->subject('Purchase Invoice') ->attachData($pdf->output(), 'purchase-tax-invoice.pdf', [ 'mime' => 'application/pdf', ]); }); return response()->json([ 'message' => __('Email Sent Successfully.'), 'redirect' => route('business.purchases.index'), ]); } public function createSupplier(Request $request) { $request->validate([ 'phone' => 'nullable|max:20|'.Rule::unique('parties')->where('business_id', auth()->user()->business_id), 'name' => 'required|string|max:255', 'type' => 'required|string|in:Retailer,Dealer,Wholesaler,Supplier', 'email' => 'nullable|email', 'image' => 'nullable|image', 'address' => 'nullable|string|max:255', 'due' => 'nullable|numeric|min:0', ]); Party::create($request->except('image', 'due') + [ 'due' => $request->due ?? 0, 'image' => $request->image ? $this->upload($request, 'image') : null, 'business_id' => auth()->user()->business_id, ]); return response()->json([ 'message' => __('Supplier created successfully'), 'redirect' => route('business.purchases.create'), ]); } public function bulkIndex() { return view('business::purchases.bulk-upload.index'); } public function bulkCartStore(Request $request) { $request->validate([ 'file' => 'required|file|mimes:xlsx,xls,csv', ]); $businessId = auth()->user()->business_id; $import = new PurchaseProductImport($businessId); try { Excel::import($import, $request->file('file')); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 422); } return response()->json([ 'success' => true, 'message' => __('Bulk upload successfully.'), 'redirect' => route('business.purchases.create'), ]); } public function viewPayment(string $id) { $purchase = Purchase::with('transactions', 'transactions.paymentType:id,name') ->where('business_id', auth()->user()->business_id) ->findOrFail($id); $transactions = $purchase->transactions->map(function ($transaction) { return [ 'date' => formatted_date($transaction->date), 'receipt_no' => $transaction->invoice_no ?? '-', 'amount' => currency_format($transaction->amount, currency: business_currency()), 'payment_type' => $transaction->transaction_type === 'bank_payment' ? (ucwords($transaction->paymentType->name) ?? '') : (ucfirst(explode('_', $transaction->transaction_type)[0] ?? '')), ]; }); return response()->json(['data' => $transactions]); } }