migrate to gtea from bistbucket
This commit is contained in:
19
app/Http/Controllers/Api/AcnooBannerController.php
Normal file
19
app/Http/Controllers/Api/AcnooBannerController.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Banner;
|
||||
|
||||
class AcnooBannerController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$banners = Banner::where('status', 1)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $banners,
|
||||
]);
|
||||
}
|
||||
}
|
||||
73
app/Http/Controllers/Api/AcnooBrandController.php
Normal file
73
app/Http/Controllers/Api/AcnooBrandController.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Brand;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AcnooBrandController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data = Brand::where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'brandName' => 'required|unique:brands,brandName,NULL,id,business_id,' . auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
$data = Brand::create($request->all() + [
|
||||
'business_id' => auth()->user()->business_id
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Brand $brand)
|
||||
{
|
||||
$request->validate([
|
||||
'brandName' => [
|
||||
'required',
|
||||
'unique:brands,brandName,' . $brand->id . ',id,business_id,' . auth()->user()->business_id,
|
||||
],
|
||||
]);
|
||||
|
||||
$brand = $brand->update($request->all());
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $brand,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Brand $brand)
|
||||
{
|
||||
$brand->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
87
app/Http/Controllers/Api/AcnooCategoryController.php
Normal file
87
app/Http/Controllers/Api/AcnooCategoryController.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AcnooCategoryController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data = Category::where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
$request->validate([
|
||||
'categoryName' => 'required|unique:categories,categoryName,NULL,id,business_id,' . $business_id,
|
||||
]);
|
||||
|
||||
$data = Category::create([
|
||||
'categoryName' => $request->categoryName,
|
||||
'variationCapacity' => $request->variationCapacity == 'true' ? 1 : 0,
|
||||
'variationColor' => $request->variationColor == 'true' ? 1 : 0,
|
||||
'variationSize' => $request->variationSize == 'true' ? 1 : 0,
|
||||
'variationType' => $request->variationType == 'true' ? 1 : 0,
|
||||
'variationWeight' => $request->variationWeight == 'true' ? 1 : 0,
|
||||
'business_id' => $business_id
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Category $category)
|
||||
{
|
||||
$request->validate([
|
||||
'categoryName' => [
|
||||
'required',
|
||||
'unique:categories,categoryName,' . $category->id . ',id,business_id,' . auth()->user()->business_id,
|
||||
],
|
||||
]);
|
||||
|
||||
$category = $category->update([
|
||||
'categoryName' => $request->categoryName,
|
||||
'variationCapacity' => $request->variationCapacity == 'true' ? 1 : 0,
|
||||
'variationColor' => $request->variationColor == 'true' ? 1 : 0,
|
||||
'variationSize' => $request->variationSize == 'true' ? 1 : 0,
|
||||
'variationType' => $request->variationType == 'true' ? 1 : 0,
|
||||
'variationWeight' => $request->variationWeight == 'true' ? 1 : 0,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $category,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Category $category)
|
||||
{
|
||||
$category->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
45
app/Http/Controllers/Api/AcnooCurrencyController.php
Normal file
45
app/Http/Controllers/Api/AcnooCurrencyController.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Currency;
|
||||
use App\Models\UserCurrency;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class AcnooCurrencyController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$currencies = Currency::orderBy('is_default', 'desc')->orderBy('status', 'desc')->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $currencies
|
||||
]);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$currency = Currency::findOrFail($id);
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$user_currency = UserCurrency::where('business_id', $business_id)->first();
|
||||
|
||||
$user_currency->update([
|
||||
'name' => $currency->name,
|
||||
'code' => $currency->code,
|
||||
'rate' => $currency->rate,
|
||||
'symbol' => $currency->symbol,
|
||||
'position' => $currency->position,
|
||||
'country_name' => $currency->country_name,
|
||||
]);
|
||||
|
||||
cache()->forget("business_currency_" . $business_id);
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message', __('Currency changed successfully'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
249
app/Http/Controllers/Api/AcnooDueController.php
Normal file
249
app/Http/Controllers/Api/AcnooDueController.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Events\DuePaymentReceived;
|
||||
use App\Events\MultiPaymentProcessed;
|
||||
use App\Models\Sale;
|
||||
use App\Models\Party;
|
||||
use App\Models\Business;
|
||||
use App\Models\Purchase;
|
||||
use App\Models\DueCollect;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Traits\DateFilterTrait;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AcnooDueController extends Controller
|
||||
{
|
||||
Use DateFilterTrait;
|
||||
|
||||
public function index()
|
||||
{
|
||||
$query = DueCollect::with('user:id,name,role', 'party:id,name,email,phone,type,address', 'branch:id,name,phone,address','transactions:id,platform,transaction_type,amount,date,invoice_no,reference_id,payment_type_id,meta', 'transactions.paymentType:id,name')
|
||||
->where('business_id', auth()->user()->business_id);
|
||||
|
||||
// Apply date filter
|
||||
if(request('duration')){
|
||||
$this->applyDateFilter($query, request('duration'), 'paymentDate', request('from_date'), request('to_date'));
|
||||
}
|
||||
|
||||
$data = $query->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$party = Party::find($request->party_id);
|
||||
|
||||
$request->validate([
|
||||
'paymentDate' => 'required|string',
|
||||
'payDueAmount' => 'nullable|numeric',
|
||||
'party_id' => 'required|exists:parties,id',
|
||||
'invoiceNumber' => 'nullable|exists:' . ($party->type == 'Supplier' ? 'purchases' : 'sales') . ',invoiceNumber',
|
||||
]);
|
||||
|
||||
$user = auth()->user();
|
||||
$action_branch_id = $user->branch_id ?? $user->active_branch_id;
|
||||
$payments = $request->payments ?? [];
|
||||
|
||||
$payDueAmount = collect($payments)
|
||||
->reject(fn($p) => strtolower($p['type'] ?? '') === 'cheque')
|
||||
->sum(fn($p) => $p['amount'] ?? 0);
|
||||
|
||||
if ($action_branch_id != $party->branch_id && !$request->invoiceNumber) {
|
||||
return response()->json([
|
||||
'message' => __('You must select an invoice when login any branch.')
|
||||
], 400);
|
||||
}
|
||||
|
||||
$branch_id = null;
|
||||
if ($request->invoiceNumber) {
|
||||
if ($party->type == 'Supplier') {
|
||||
$invoice = Purchase::where('invoiceNumber', $request->invoiceNumber)->where('party_id', $request->party_id)->first();
|
||||
} else {
|
||||
$invoice = Sale::where('invoiceNumber', $request->invoiceNumber)->where('party_id', $request->party_id)->first();
|
||||
}
|
||||
|
||||
if (!isset($invoice)) {
|
||||
return response()->json([
|
||||
'message' => 'Invoice Not Found.'
|
||||
], 404);
|
||||
}
|
||||
|
||||
if (!auth()->user()->active_branch) {
|
||||
if (isset($invoice) && isset($invoice->branch_id)) {
|
||||
$branch_id = $invoice->branch_id;
|
||||
}
|
||||
}
|
||||
|
||||
if ($invoice->dueAmount < $request->payDueAmount) {
|
||||
return response()->json([
|
||||
'message' => 'Invoice due is ' . $invoice->dueAmount . '. You can not pay more then the invoice due amount.'
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$request->invoiceNumber) {
|
||||
if ($party->type == 'Supplier') {
|
||||
$all_invoice_due = Purchase::where('party_id', $request->party_id)->sum('dueAmount');
|
||||
} else {
|
||||
$all_invoice_due = Sale::where('party_id', $request->party_id)->sum('dueAmount');
|
||||
}
|
||||
|
||||
if (($all_invoice_due + $request->payDueAmount) > $party->due) {
|
||||
return response()->json([
|
||||
'message' => __('You can pay only '. $party->due - $all_invoice_due .', without selecting an invoice.')
|
||||
], 400);
|
||||
}
|
||||
|
||||
if ($party->opening_balance_type == 'due') {
|
||||
$party->update([
|
||||
'opening_balance' => max(0, $party->opening_balance - $payDueAmount),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$data = DueCollect::create($request->all() + [
|
||||
'user_id' => auth()->id(),
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'sale_id' => $party->type != 'Supplier' && isset($invoice) ? $invoice->id : NULL,
|
||||
'purchase_id' => $party->type == 'Supplier' && isset($invoice) ? $invoice->id : NULL,
|
||||
'totalDue' => isset($invoice) ? $invoice->dueAmount : $party->due,
|
||||
'dueAmountAfterPay' => isset($invoice) ? ($invoice->dueAmount - $payDueAmount) : ($party->due - $payDueAmount),
|
||||
]);
|
||||
|
||||
if (isset($invoice)) {
|
||||
$invoice->update([
|
||||
'dueAmount' => $invoice->dueAmount - $payDueAmount
|
||||
]);
|
||||
}
|
||||
|
||||
$party->type == 'Supplier' ? updateBalance($payDueAmount, 'decrement', $branch_id) : updateBalance($payDueAmount, 'increment', $branch_id);
|
||||
|
||||
$party->update([
|
||||
'due' => $party->due - $payDueAmount
|
||||
]);
|
||||
|
||||
// MultiPaymentProcessed Event
|
||||
event(new MultiPaymentProcessed(
|
||||
$payments,
|
||||
$data->id,
|
||||
'due_collect',
|
||||
$payDueAmount,
|
||||
$party->id
|
||||
));
|
||||
|
||||
// Send SMS
|
||||
event(new DuePaymentReceived($data));
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data->load('user:id,name,role', 'party:id,name,email,phone,type,address', 'branch:id,name,phone,address','transactions:id,platform,transaction_type,amount,date,invoice_no,reference_id,payment_type_id,meta', 'transactions.paymentType:id,name'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function invoiceWiseDue()
|
||||
{
|
||||
$data = Sale::select('id','dueAmount', 'paidAmount', 'totalAmount', 'invoiceNumber', 'saleDate', 'meta')
|
||||
->where('business_id', auth()->user()->business_id)
|
||||
->whereNull('party_id')
|
||||
->where('dueAmount', '>', 0)
|
||||
->latest()->paginate(10);
|
||||
|
||||
// Sum only for paginate data
|
||||
$total_receivable = $data->getCollection()->sum('dueAmount');
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'total_receivable' => (float) $total_receivable,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function collectInvoiceDue(Request $request)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$request->validate([
|
||||
'paymentDate' => 'required|string',
|
||||
'payDueAmount' => 'nullable|numeric',
|
||||
'invoiceNumber' => 'required|string|exists:sales,invoiceNumber',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$invoice = Sale::where('business_id', $business_id)->where('invoiceNumber', $request->invoiceNumber)->whereNull('party_id')->first();
|
||||
|
||||
if (!$invoice) {
|
||||
return response()->json([
|
||||
'message' => 'Invoice Not Found.'
|
||||
], 404);
|
||||
}
|
||||
$payments = $request->payments ?? [];
|
||||
|
||||
$payDueAmount = collect($payments)
|
||||
->reject(fn($p) => strtolower($p['type'] ?? '') === 'cheque')
|
||||
->sum(fn($p) => $p['amount'] ?? 0);
|
||||
|
||||
|
||||
if ($invoice->dueAmount < $payDueAmount) {
|
||||
return response()->json([
|
||||
'message' => 'Invoice due is ' . $invoice->dueAmount . '. You cannot pay more than the invoice due amount.'
|
||||
], 400);
|
||||
}
|
||||
|
||||
$data = DueCollect::create([
|
||||
'user_id' => auth()->id(),
|
||||
'business_id' => $business_id,
|
||||
'sale_id' => $invoice->id,
|
||||
'invoiceNumber' => $request->invoiceNumber,
|
||||
'totalDue' => $invoice->dueAmount,
|
||||
'dueAmountAfterPay' => $invoice->dueAmount - $payDueAmount,
|
||||
'payDueAmount' => $payDueAmount,
|
||||
'payment_type_id' => $request->payment_type_id,
|
||||
'paymentDate' => $request->paymentDate,
|
||||
]);
|
||||
|
||||
$invoice->update([
|
||||
'dueAmount' => $invoice->dueAmount - $payDueAmount
|
||||
]);
|
||||
|
||||
$business = Business::findOrFail($business_id);
|
||||
$business->update([
|
||||
'remainingShopBalance' => $business->remainingShopBalance + $payDueAmount
|
||||
]);
|
||||
|
||||
sendNotifyToUser($data->id, route('business.dues.index', ['id' => $data->id]), __('Due Collection has been created.'), $business_id);
|
||||
|
||||
// MultiPaymentProcessed Event
|
||||
event(new MultiPaymentProcessed(
|
||||
$payments,
|
||||
$data->id,
|
||||
'due_collect',
|
||||
$payDueAmount,
|
||||
));
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data->load('user:id,name', 'party:id,name,email,phone,type,address','transactions:id,platform,transaction_type,amount,date,invoice_no,reference_id,payment_type_id,meta', 'transactions.paymentType:id,name'),
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json([
|
||||
'message' => 'Something went wrong!',
|
||||
'error' => $e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
105
app/Http/Controllers/Api/AcnooExpenseController.php
Normal file
105
app/Http/Controllers/Api/AcnooExpenseController.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Expense;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Events\MultiPaymentProcessed;
|
||||
use App\Traits\DateFilterTrait;
|
||||
|
||||
class AcnooExpenseController extends Controller
|
||||
{
|
||||
use DateFilterTrait;
|
||||
public function index(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
|
||||
$expenseQuery = Expense::with(['category:id,categoryName', 'payment_type:id,name', 'branch:id,name'])->where('business_id', $businessId);
|
||||
|
||||
$expenseQuery->when($request->branch_id, function ($q) use ($request) {
|
||||
$q->where('branch_id', $request->branch_id);
|
||||
});
|
||||
|
||||
// Apply date filter
|
||||
if(request('duration')){
|
||||
$this->applyDateFilter($expenseQuery, request('duration'), 'expenseDate', request('from_date'), request('to_date'));
|
||||
}
|
||||
|
||||
// Search Filter
|
||||
if ($request->filled('search')) {
|
||||
$expenseQuery->where(function ($query) use ($request) {
|
||||
$query->where('expanseFor', 'like', '%' . $request->search . '%')
|
||||
->orWhere('paymentType', 'like', '%' . $request->search . '%')
|
||||
->orWhere('referenceNo', 'like', '%' . $request->search . '%')
|
||||
->orWhere('amount', 'like', '%' . $request->search . '%')
|
||||
->orWhereHas('category', function ($q) use ($request) {
|
||||
$q->where('categoryName', '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 . '%');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$data = $expenseQuery->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'expense_category_id' => 'required|exists:expense_categories,id',
|
||||
'expanseFor' => 'nullable|string',
|
||||
'referenceNo' => 'nullable|string',
|
||||
'note' => 'nullable|string',
|
||||
'payments' => 'required|array|min:1',
|
||||
'payments.*.type' => 'required|string',
|
||||
'payments.*.amount' => 'nullable|numeric|min:0',
|
||||
], [
|
||||
'payments.required' => 'At least one payment method is required.',
|
||||
'payments.*.type.required' => 'Each payment must have a type.',
|
||||
'payments.*.amount.numeric' => 'Each payment amount must be numeric.',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
||||
updateBalance($request->amount, 'decrement');
|
||||
|
||||
$data = Expense::create($request->except('status','paymentType') + [
|
||||
'user_id' => auth()->id(),
|
||||
'business_id' => auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
event(new MultiPaymentProcessed(
|
||||
$request->payments ?? [],
|
||||
$data->id,
|
||||
'expense',
|
||||
$request->amount ?? 0,
|
||||
));
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
76
app/Http/Controllers/Api/AcnooIncomeCategoryController.php
Normal file
76
app/Http/Controllers/Api/AcnooIncomeCategoryController.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\IncomeCategory;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AcnooIncomeCategoryController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = IncomeCategory::where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'categoryName' => 'required|unique:income_categories,categoryName,NULL,id,business_id,' . auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
$data = IncomeCategory::create($request->except('status') + [
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'status' => $request->status == 'true' ? 1 : 0,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$category = IncomeCategory::findOrFail($id);
|
||||
|
||||
$request->validate([
|
||||
'categoryName' => [
|
||||
'required',
|
||||
'unique:income_categories,categoryName,' . $category->id . ',id,business_id,' . auth()->user()->business_id,
|
||||
],
|
||||
]);
|
||||
|
||||
$category->update($request->except('status') + [
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'status' => $request->status == 'true' ? 1 : 0,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data updated successfully.'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$category = IncomeCategory::findOrFail($id);
|
||||
$category->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
106
app/Http/Controllers/Api/AcnooIncomeController.php
Normal file
106
app/Http/Controllers/Api/AcnooIncomeController.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Income;
|
||||
use App\Traits\DateFilterTrait;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Events\MultiPaymentProcessed;
|
||||
|
||||
class AcnooIncomeController extends Controller
|
||||
{
|
||||
Use DateFilterTrait;
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
|
||||
$incomeQuery = Income::with(['category:id,categoryName', 'payment_type:id,name', 'branch:id,name'])->where('business_id', $businessId);
|
||||
|
||||
// Branch filter
|
||||
if ($request->branch_id) {
|
||||
$incomeQuery->where('branch_id', $request->branch_id);
|
||||
}
|
||||
|
||||
// Apply date filter
|
||||
if(request('duration')){
|
||||
$this->applyDateFilter($incomeQuery, request('duration'), 'incomeDate', request('from_date'), request('to_date'));
|
||||
}
|
||||
|
||||
// Search filter
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$incomeQuery->where(function ($query) use ($search) {
|
||||
$query->where('incomeFor', 'like', '%' . $search . '%')
|
||||
->orWhere('paymentType', 'like', '%' . $search . '%')
|
||||
->orWhere('amount', 'like', '%' . $search . '%')
|
||||
->orWhere('referenceNo', 'like', '%' . $search . '%')
|
||||
->orWhereHas('category', fn($q) => $q->where('categoryName', 'like', '%' . $search . '%'))
|
||||
->orWhereHas('payment_type', fn($q) => $q->where('name', 'like', '%' . $search . '%'))
|
||||
->orWhereHas('branch', fn($q) => $q->where('name', 'like', '%' . $search . '%'));
|
||||
});
|
||||
}
|
||||
|
||||
$data = $incomeQuery->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'income_category_id' => 'required|exists:income_categories,id',
|
||||
'incomeFor' => 'nullable|string',
|
||||
'referenceNo' => 'nullable|string',
|
||||
'note' => 'nullable|string',
|
||||
'payments' => 'required|array|min:1',
|
||||
'payments.*.type' => 'required|string',
|
||||
'payments.*.amount' => 'nullable|numeric|min:0',
|
||||
'payments.*.cheque_number' => 'nullable|string',
|
||||
], [
|
||||
'payments.required' => 'At least one payment method is required.',
|
||||
'payments.*.type.required' => 'Each payment must have a type.',
|
||||
'payments.*.amount.numeric' => 'Each payment amount must be numeric.',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$total_amount = collect($request->payments)
|
||||
->reject(fn($p) => strtolower($p['type'] ?? '') == 'cheque')
|
||||
->sum(fn($p) => $p['amount'] ?? 0);
|
||||
|
||||
updateBalance($total_amount, 'decrement');
|
||||
|
||||
$data = Income::create($request->except('status', 'amount', 'paymentType') + [
|
||||
'user_id' => auth()->id(),
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'amount' => $total_amount,
|
||||
]);
|
||||
|
||||
event(new MultiPaymentProcessed(
|
||||
$request->payments ?? [],
|
||||
$data->id,
|
||||
'income',
|
||||
$total_amount ?? 0,
|
||||
));
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
app/Http/Controllers/Api/AcnooInvoiceController.php
Normal file
64
app/Http/Controllers/Api/AcnooInvoiceController.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\DueCollect;
|
||||
use App\Models\Party;
|
||||
use App\Models\Purchase;
|
||||
use App\Models\Sale;
|
||||
use App\Models\SaleReturn;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AcnooInvoiceController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'party_id' => 'required|exists:parties,id'
|
||||
]);
|
||||
|
||||
$party = Party::select('id', 'due', 'name', 'type')->find(request('party_id'));
|
||||
|
||||
if ($party->type == 'Supplier')
|
||||
{
|
||||
$data = $party->load('purchases_dues:id,party_id,dueAmount,paidAmount,totalAmount,invoiceNumber');
|
||||
} else {
|
||||
$data = $party->load('sales_dues:id,party_id,dueAmount,paidAmount,totalAmount,invoiceNumber');
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function newInvoice(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'platform' => 'required|in:sales,purchases,due_collects,sales_return,purchases_return'
|
||||
]);
|
||||
|
||||
if ($request->platform == 'sales') {
|
||||
$prefix = 'S-';
|
||||
$id = Sale::where('business_id', auth()->user()->business_id)->count();
|
||||
} elseif ($request->platform == 'purchases') {
|
||||
$prefix = 'P-';
|
||||
$id = Purchase::where('business_id', auth()->user()->business_id)->count();
|
||||
} elseif ($request->platform == 'sales_return') {
|
||||
$prefix = 'SR-';
|
||||
$id = SaleReturn::where('business_id', auth()->user()->business_id)->count();
|
||||
} elseif ($request->platform == 'purchases_return') {
|
||||
// $prefix = 'PR-';
|
||||
// $id = Purchase::where('business_id', auth()->user()->business_id)->count();
|
||||
}
|
||||
else {
|
||||
$prefix = 'D-';
|
||||
$id = DueCollect::where('business_id', auth()->user()->business_id)->count();
|
||||
}
|
||||
|
||||
$invoice = $prefix . str_pad($id + 1, 5, '0', STR_PAD_LEFT);
|
||||
|
||||
return response()->json($invoice);
|
||||
}
|
||||
}
|
||||
33
app/Http/Controllers/Api/AcnooLanguageController.php
Normal file
33
app/Http/Controllers/Api/AcnooLanguageController.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AcnooLanguageController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = json_decode(file_get_contents(base_path('lang/langlist.json')), true);
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'lang' => 'required|max:30|min:1|string'
|
||||
]);
|
||||
|
||||
auth()->user()->update([
|
||||
'lang' => $request->lang
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Language updated successfully.')
|
||||
]);
|
||||
}
|
||||
}
|
||||
18
app/Http/Controllers/Api/AcnooPrivacyController.php
Normal file
18
app/Http/Controllers/Api/AcnooPrivacyController.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Option;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class AcnooPrivacyController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$policy = Option::where('key', 'policy')->first()->value;
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $policy ?? 'Privacy Policy',
|
||||
]);
|
||||
}
|
||||
}
|
||||
485
app/Http/Controllers/Api/AcnooProductController.php
Normal file
485
app/Http/Controllers/Api/AcnooProductController.php
Normal file
@@ -0,0 +1,485 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\ComboProduct;
|
||||
use App\Models\Stock;
|
||||
use App\Models\Product;
|
||||
use App\Helpers\HasUploader;
|
||||
use App\Models\Vat;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class AcnooProductController extends Controller
|
||||
{
|
||||
use HasUploader;
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
$products = Product::with([
|
||||
'unit:id,unitName',
|
||||
'vat:id,rate',
|
||||
'brand:id,brandName',
|
||||
'category:id,categoryName',
|
||||
'product_model:id,name',
|
||||
'stocks',
|
||||
'combo_products.stock.product' => function ($query) {
|
||||
$query->select('id', 'productName', 'productCode');
|
||||
},
|
||||
'rack:id,name',
|
||||
'shelf:id,name'
|
||||
])
|
||||
->where(function ($query) {
|
||||
$query->where(function ($q) {
|
||||
$q->where('product_type', '!=', 'combo')
|
||||
->whereHas('stocks');
|
||||
})
|
||||
->orWhere(function ($q) {
|
||||
$q->where('product_type', 'combo')
|
||||
->whereHas('combo_products');
|
||||
});
|
||||
})
|
||||
->withSum('saleDetails', 'quantities')
|
||||
->withSum('purchaseDetails', 'quantities')
|
||||
->withSum('stocks', 'productStock')
|
||||
->where('business_id', $user->business_id)
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$total_stock_value = $products->sum(function ($product) {
|
||||
return $product->stocks->sum(function ($stock) {
|
||||
return $stock->productPurchasePrice * $stock->productStock;
|
||||
});
|
||||
});
|
||||
|
||||
$products = $products->map(function ($product) {
|
||||
$product->total_sale_amount = $product->saleDetails->sum(function ($sale) {
|
||||
return ($sale->price - $sale->discount) * $sale->quantities;
|
||||
});
|
||||
|
||||
$product->total_profit_loss = $product->saleDetails->sum(function ($sale) {
|
||||
// If lossProfit column exists, use it
|
||||
if (!is_null($sale->lossProfit)) {
|
||||
return $sale->lossProfit;
|
||||
}
|
||||
// Otherwise calculate: (price - discount - purchase price) * quantity
|
||||
return (($sale->price - $sale->discount) - $sale->productPurchasePrice) * $sale->quantities;
|
||||
});
|
||||
|
||||
return $product;
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'total_stock_value' => $total_stock_value,
|
||||
'data' => $products,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
if (is_string($request->stocks)) {
|
||||
$request->merge(['stocks' => json_decode($request->stocks, true)]);
|
||||
}
|
||||
|
||||
if (is_string($request->warranty_guarantee_info)) {
|
||||
$request->merge(['warranty_guarantee_info' => json_decode($request->warranty_guarantee_info, true)]);
|
||||
}
|
||||
|
||||
if (is_string($request->combo_products)) {
|
||||
$request->merge(['combo_products' => json_decode($request->combo_products, true)]);
|
||||
}
|
||||
|
||||
if (is_string($request->variation_ids)) {
|
||||
$decoded = json_decode($request->variation_ids, true);
|
||||
$request->merge(['variation_ids' => $decoded]);
|
||||
}
|
||||
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$request->validate([
|
||||
'vat_id' => 'nullable|exists:vats,id',
|
||||
'unit_id' => 'nullable|exists:units,id',
|
||||
'brand_id' => 'nullable|exists:brands,id',
|
||||
'category_id' => 'nullable|exists:categories,id',
|
||||
'model_id' => 'nullable|exists:product_models,id',
|
||||
'vat_type' => 'nullable|in:inclusive,exclusive',
|
||||
'productName' => 'required|string|max:255',
|
||||
'productPicture' => 'nullable|image|mimes:jpg,png,jpeg,svg',
|
||||
'productCode' => [
|
||||
'nullable',
|
||||
Rule::unique('products')->where(function ($query) use ($business_id) {
|
||||
return $query->where('business_id', $business_id);
|
||||
}),
|
||||
],
|
||||
'alert_qty' => 'nullable|numeric|min:0',
|
||||
'size' => 'nullable|string|max:255',
|
||||
'type' => 'nullable|string|max:255',
|
||||
'color' => 'nullable|string|max:255',
|
||||
'weight' => 'nullable|string|max:255',
|
||||
'capacity' => 'nullable|string|max:255',
|
||||
'productManufacturer' => 'nullable|string|max:255',
|
||||
'product_type' => 'required|in:single,variant,combo',
|
||||
'variation_ids' => 'nullable|array',
|
||||
'variation_ids.*' => 'exists:variations,id',
|
||||
|
||||
'stocks.*.warehouse_id' => 'nullable|exists:warehouses,id',
|
||||
'stocks.*.productStock' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.exclusive_price' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.inclusive_price' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.profit_percent' => 'nullable|numeric|max:99999999.99',
|
||||
'stocks.*.productSalePrice' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.productWholeSalePrice' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.productDealerPrice' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.mfg_date' => 'nullable|date',
|
||||
'stocks.*.expire_date' => 'nullable|date|after_or_equal:stocks.*.mfg_date',
|
||||
'stocks' => 'nullable|array',
|
||||
'stocks.*.batch_no' => [
|
||||
'nullable',
|
||||
function ($attribute, $value, $fail) use ($request) {
|
||||
$batchNos = collect($request->stocks)->pluck('batch_no')->filter()->toArray();
|
||||
|
||||
if (count($batchNos) !== count(array_unique($batchNos))) {
|
||||
$fail('Duplicate batch number found in the request.');
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
// Combo validation
|
||||
'combo_products' => 'nullable|array',
|
||||
'combo_products.*.stock_id' => [
|
||||
'required_if:product_type,combo',
|
||||
Rule::exists('stocks', 'id')->where('business_id', $business_id),
|
||||
],
|
||||
'combo_products.*.quantity' => 'required_if:product_type,combo|numeric|min:1',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// vat calculation
|
||||
$vat = Vat::find($request->vat_id);
|
||||
$vat_rate = $vat->rate ?? 0;
|
||||
|
||||
// Create the product
|
||||
$product = Product::create($request->only('productName', 'unit_id', 'brand_id', 'vat_id', 'vat_type', 'category_id', 'productCode', 'product_type', 'rack_id', 'shelf_id', 'model_id', 'variation_ids', 'warranty_guarantee_info') + [
|
||||
'business_id' => $business_id,
|
||||
'alert_qty' => $request->alert_qty ?? 0,
|
||||
'profit_percent' => $request->product_type == 'combo' ? $request->profit_percent ?? 0 : 0,
|
||||
'productSalePrice' => $request->product_type == 'combo' ? $request->productSalePrice ?? 0 : 0,
|
||||
'productPicture' => $request->productPicture ? $this->upload($request, 'productPicture') : NULL,
|
||||
]);
|
||||
|
||||
// Single or Variant Product
|
||||
if (in_array($request->product_type, ['single', 'variant']) && !empty($request->stocks)) {
|
||||
$stockData = [];
|
||||
foreach ($request->stocks as $stock) {
|
||||
|
||||
$base_price = $stock['exclusive_price'] ?? 0;
|
||||
$purchasePrice = $request->vat_type === 'inclusive'
|
||||
? $base_price + ($base_price * $vat_rate / 100)
|
||||
: $base_price;
|
||||
|
||||
$stockData[] = [
|
||||
'business_id' => $business_id,
|
||||
'product_id' => $product->id,
|
||||
'batch_no' => $stock['batch_no'] ?? null,
|
||||
'warehouse_id' => $stock['warehouse_id'] ?? null,
|
||||
'productStock' => $stock['productStock'] ?? 0,
|
||||
'productPurchasePrice' => $purchasePrice,
|
||||
'profit_percent' => $stock['profit_percent'] ?? 0,
|
||||
'productSalePrice' => $stock['productSalePrice'] ?? 0,
|
||||
'productWholeSalePrice' => $stock['productWholeSalePrice'] ?? 0,
|
||||
'productDealerPrice' => $stock['productDealerPrice'] ?? 0,
|
||||
'mfg_date' => $stock['mfg_date'] ?? null,
|
||||
'expire_date' => $stock['expire_date'] ?? null,
|
||||
'variation_data' => isset($stock['variation_data']) ? json_encode($stock['variation_data']) : null,
|
||||
'variant_name' => $stock['variant_name'] ?? null,
|
||||
'serial_numbers' => $stock['serial_numbers'] ?? null,
|
||||
'branch_id' => auth()->user()->branch_id ?? auth()->user()->active_branch_id,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
Stock::insert($stockData);
|
||||
}
|
||||
|
||||
// Combo Product
|
||||
if ($request->product_type === 'combo' && !empty($request->combo_products)) {
|
||||
foreach ($request->combo_products as $item) {
|
||||
ComboProduct::create([
|
||||
'product_id' => $product->id,
|
||||
'stock_id' => $item['stock_id'],
|
||||
'quantity' => $item['quantity'],
|
||||
'purchase_price' => $item['purchase_price'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $product
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json([
|
||||
'message' => $e->getMessage(),
|
||||
], 406);
|
||||
}
|
||||
}
|
||||
|
||||
public function show(string $id)
|
||||
{
|
||||
$data = Product::with([
|
||||
'unit:id,unitName',
|
||||
'vat:id,rate',
|
||||
'brand:id,brandName',
|
||||
'category:id,categoryName',
|
||||
'product_model:id,name',
|
||||
'stocks',
|
||||
'stocks.warehouse:id,name',
|
||||
'combo_products.stock.product' => function ($query) {
|
||||
$query->select('id', 'productName', 'productCode');
|
||||
},
|
||||
'rack:id,name',
|
||||
'shelf:id,name'
|
||||
])
|
||||
->withSum('saleDetails', 'quantities')
|
||||
->withSum('purchaseDetails', 'quantities')
|
||||
->withSum('stocks', 'productStock')
|
||||
->findOrFail($id);
|
||||
|
||||
$data->total_sale_amount = $data->saleDetails->sum(function ($sale) {
|
||||
return ($sale->price - $sale->discount) * $sale->quantities;
|
||||
});
|
||||
|
||||
$data->total_profit_loss = $data->saleDetails->sum(function ($sale) {
|
||||
if (!is_null($sale->lossProfit)) {
|
||||
return $sale->lossProfit;
|
||||
}
|
||||
return (($sale->price - $sale->discount) - $sale->productPurchasePrice) * $sale->quantities;
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Product $product)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
if ($product->product_type != $request->product_type) {
|
||||
return response()->json([
|
||||
'message' => __('Product type can not be changed.'),
|
||||
], 406);
|
||||
}
|
||||
|
||||
if (is_string($request->stocks)) {
|
||||
$request->merge(['stocks' => json_decode($request->stocks, true)]);
|
||||
}
|
||||
|
||||
if (is_string($request->warranty_guarantee_info)) {
|
||||
$request->merge(['warranty_guarantee_info' => json_decode($request->warranty_guarantee_info, true)]);
|
||||
}
|
||||
|
||||
if (is_string($request->combo_products)) {
|
||||
$request->merge(['combo_products' => json_decode($request->combo_products, true)]);
|
||||
}
|
||||
|
||||
if (is_string($request->variation_ids)) {
|
||||
$request->merge(['variation_ids' => json_decode($request->variation_ids, true)]);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'vat_id' => 'nullable|exists:vats,id',
|
||||
'unit_id' => 'nullable|exists:units,id',
|
||||
'brand_id' => 'nullable|exists:brands,id',
|
||||
'category_id' => 'nullable|exists:categories,id',
|
||||
'model_id' => 'nullable|exists:product_models,id',
|
||||
'vat_type' => 'nullable|in:inclusive,exclusive',
|
||||
'productName' => 'required|string|max:255',
|
||||
'productPicture' => 'nullable|image|mimes:jpg,png,jpeg,svg',
|
||||
'productCode' => [
|
||||
'nullable',
|
||||
Rule::unique('products', 'productCode')->ignore($product->id)->where(function ($query) use ($business_id) {
|
||||
return $query->where('business_id', $business_id);
|
||||
}),
|
||||
],
|
||||
'alert_qty' => 'nullable|numeric|min:0',
|
||||
'size' => 'nullable|string|max:255',
|
||||
'type' => 'nullable|string|max:255',
|
||||
'color' => 'nullable|string|max:255',
|
||||
'weight' => 'nullable|string|max:255',
|
||||
'capacity' => 'nullable|string|max:255',
|
||||
'productManufacturer' => 'nullable|string|max:255',
|
||||
'product_type' => 'required|in:single,variant,combo',
|
||||
'variation_ids' => 'nullable|array',
|
||||
'variation_ids.*' => 'exists:variations,id',
|
||||
|
||||
'stocks.*.warehouse_id' => 'nullable|exists:warehouses,id',
|
||||
'stocks.*.productStock' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.exclusive_price' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.inclusive_price' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.profit_percent' => 'nullable|numeric|max:99999999.99',
|
||||
'stocks.*.productSalePrice' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.productWholeSalePrice' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.productDealerPrice' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'stocks.*.mfg_date' => 'nullable|date',
|
||||
'stocks.*.expire_date' => 'nullable|date|after_or_equal:stocks.*.mfg_date',
|
||||
'stocks' => 'nullable|array',
|
||||
'stocks.*.batch_no' => [
|
||||
'nullable',
|
||||
function ($attribute, $value, $fail) use ($request) {
|
||||
$batchNos = collect($request->stocks)->pluck('batch_no')->filter()->toArray();
|
||||
|
||||
if (count($batchNos) !== count(array_unique($batchNos))) {
|
||||
$fail('Duplicate batch number found in the request.');
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
// Combo validation
|
||||
'combo_products' => 'nullable|array',
|
||||
'combo_products.*.stock_id' => [
|
||||
'required_if:product_type,combo',
|
||||
Rule::exists('stocks', 'id')->where('business_id', $business_id),
|
||||
],
|
||||
'combo_products.*.quantity' => 'required_if:product_type,combo|numeric|min:1',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// VAT calculation
|
||||
$vat = Vat::find($request->vat_id);
|
||||
$vat_rate = $vat->rate ?? 0;
|
||||
|
||||
// Update product
|
||||
$product->update($request->except(['productPicture', 'productPurchasePrice', 'productDealerPrice', 'productWholeSalePrice', 'alert_qty', 'stocks', 'vat_amount', 'profit_percent']) + [
|
||||
'business_id' => $business_id,
|
||||
'alert_qty' => $request->alert_qty ?? 0,
|
||||
'profit_percent' => $request->profit_percent ?? 0,
|
||||
'productPicture' => $request->productPicture ? $this->upload($request, 'productPicture', $product->productPicture) : $product->productPicture,
|
||||
]);
|
||||
|
||||
// Delete previous stocks and combos
|
||||
if ($product->product_type === 'combo') {
|
||||
ComboProduct::where('product_id', $product->id)->delete();
|
||||
}
|
||||
|
||||
// Handle Single/Variant Product Stocks
|
||||
if (in_array($request->product_type, ['single', 'variant']) && !empty($request->stocks)) {
|
||||
$existingStockIds = $product->stocks()->pluck('id')->toArray();
|
||||
$incomingStockIds = collect($request->stocks)->pluck('stock_id')->filter()->toArray();
|
||||
|
||||
// Delete removed stocks
|
||||
$stocksToDelete = array_diff($existingStockIds, $incomingStockIds);
|
||||
Stock::whereIn('id', $stocksToDelete)->delete();
|
||||
|
||||
// Insert or Update
|
||||
foreach ($request->stocks as $stock) {
|
||||
// Decode variation_data if sent as string
|
||||
if (isset($stock['variation_data']) && is_string($stock['variation_data'])) {
|
||||
$stock['variation_data'] = json_decode($stock['variation_data'], true);
|
||||
}
|
||||
|
||||
$stockId = $stock['stock_id'] ?? null;
|
||||
|
||||
// Recalculate price
|
||||
$base_price = $stock['exclusive_price'] ?? 0;
|
||||
$purchasePrice = $request->vat_type === 'inclusive'
|
||||
? $base_price + ($base_price * $vat_rate / 100)
|
||||
: $base_price;
|
||||
|
||||
$payload = [
|
||||
'business_id' => $business_id,
|
||||
'product_id' => $product->id,
|
||||
'batch_no' => $stock['batch_no'] ?? null,
|
||||
'warehouse_id' => $stock['warehouse_id'] ?? null,
|
||||
'productStock' => $stock['productStock'] ?? 0,
|
||||
'productPurchasePrice' => $purchasePrice,
|
||||
'profit_percent' => $stock['profit_percent'] ?? 0,
|
||||
'productSalePrice' => $stock['productSalePrice'] ?? 0,
|
||||
'productWholeSalePrice' => $stock['productWholeSalePrice'] ?? 0,
|
||||
'productDealerPrice' => $stock['productDealerPrice'] ?? 0,
|
||||
'mfg_date' => $stock['mfg_date'] ?? null,
|
||||
'expire_date' => $stock['expire_date'] ?? null,
|
||||
'variation_data' => $stock['variation_data'] ?? null,
|
||||
'variant_name' => $stock['variant_name'] ?? null,
|
||||
'branch_id' => auth()->user()->branch_id ?? auth()->user()->active_branch_id,
|
||||
'serial_numbers' => $request->has_serial ? $stock['serial_numbers'] : null,
|
||||
];
|
||||
|
||||
if ($stockId) {
|
||||
Stock::where('id', $stockId)->update($payload);
|
||||
} else {
|
||||
Stock::create($payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Combo Product
|
||||
if ($request->product_type === 'combo' && !empty($request->combo_products)) {
|
||||
foreach ($request->combo_products as $item) {
|
||||
ComboProduct::create([
|
||||
'product_id' => $product->id,
|
||||
'stock_id' => $item['stock_id'],
|
||||
'quantity' => $item['quantity'],
|
||||
'purchase_price' => $item['purchase_price'] ?? 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $product->load('stocks'),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json([
|
||||
'message' => $e->getMessage()
|
||||
], 406);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Product $product)
|
||||
{
|
||||
if (file_exists($product->productPicture)) {
|
||||
Storage::delete($product->productPicture);
|
||||
}
|
||||
$product->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function generateCode()
|
||||
{
|
||||
$product_id = (Product::where('business_id', auth()->user()->business_id)->count() ?? 0) + 1;
|
||||
$code = str_pad($product_id, 4, '0', STR_PAD_LEFT);
|
||||
|
||||
return response()->json([
|
||||
'data' => $code,
|
||||
]);
|
||||
}
|
||||
}
|
||||
83
app/Http/Controllers/Api/AcnooProfileController.php
Normal file
83
app/Http/Controllers/Api/AcnooProfileController.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Helpers\HasUploader;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class AcnooProfileController extends Controller
|
||||
{
|
||||
use HasUploader;
|
||||
|
||||
public function index()
|
||||
{
|
||||
$user = User::with('business')->findOrFail(auth()->id());
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $user
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|max:250',
|
||||
'email' => ['required', 'email', Rule::unique('users')->ignore(auth()->id())],
|
||||
'image' => 'nullable|image|mimes:jpeg,png,gif|dimensions:max_width=2000,max_height=2000|max:1048',
|
||||
]);
|
||||
|
||||
$user = User::findOrFail(auth()->id());
|
||||
|
||||
$user->update($request->except('image') + [
|
||||
'image' => $request->image ? $this->upload($request, 'image', $user->image) : $user->image,
|
||||
]);
|
||||
|
||||
$user = User::findOrFail(auth()->id());
|
||||
|
||||
$data = [
|
||||
'name' => $user->name,
|
||||
'email' => $user->email,
|
||||
'phone' => $user->phone,
|
||||
'image' => $user->image,
|
||||
];
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Profile updated successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function changePassword(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'current_password' => 'required',
|
||||
'password' => 'required|string|min:6',
|
||||
]);
|
||||
|
||||
$user = auth()->user();
|
||||
|
||||
if ($request->current_password == $request->password){
|
||||
return response()->json([
|
||||
'message' => __('You have already used this password.')
|
||||
], 422);
|
||||
}
|
||||
|
||||
if (!Hash::check($request->current_password, $user->password)) {
|
||||
return response()->json([
|
||||
'message' => __('Current password does not match with old password.')
|
||||
], 422);
|
||||
}
|
||||
|
||||
$user->update([
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Password changed successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
534
app/Http/Controllers/Api/AcnooReportController.php
Normal file
534
app/Http/Controllers/Api/AcnooReportController.php
Normal file
@@ -0,0 +1,534 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Vat;
|
||||
use App\Models\Sale;
|
||||
use App\Models\Product;
|
||||
use App\Models\Purchase;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\PlanSubscribe;
|
||||
use App\Traits\DateFilterTrait;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class AcnooReportController extends Controller
|
||||
{
|
||||
use DateFilterTrait;
|
||||
|
||||
public function lossProfit(Request $request)
|
||||
{
|
||||
$user = auth()->user();
|
||||
$businessId = $user->business_id;
|
||||
|
||||
$branchId = null;
|
||||
if (moduleCheck('MultiBranchAddon')) {
|
||||
$branchId = $user->branch_id ?? $user->active_branch_id;
|
||||
}
|
||||
|
||||
$duration = $request->duration ?: 'today';
|
||||
|
||||
|
||||
$salesQuery = DB::table('sales')
|
||||
->select(
|
||||
DB::raw('DATE(saleDate) as date'),
|
||||
DB::raw('SUM(actual_total_amount) as total_sales'),
|
||||
DB::raw('SUM(lossProfit) as total_sale_income')
|
||||
)
|
||||
->where('business_id', $businessId)
|
||||
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
|
||||
->groupBy(DB::raw('DATE(saleDate)'));
|
||||
|
||||
|
||||
$this->applyDateFilter($salesQuery, $duration, 'saleDate', $request->from_date, $request->to_date);
|
||||
|
||||
|
||||
$dailySales = $salesQuery->get();
|
||||
|
||||
$sale_datas = $dailySales->map(fn($sale) => (object)[
|
||||
'type' => 'Sale',
|
||||
'date' => $sale->date,
|
||||
'total_sales' => $sale->total_sales,
|
||||
'total_incomes' => $sale->total_sale_income,
|
||||
]);
|
||||
|
||||
$incomeQuery = DB::table('incomes')
|
||||
->select(
|
||||
DB::raw('DATE(incomeDate) as date'),
|
||||
DB::raw('SUM(amount) as total_incomes')
|
||||
)
|
||||
->where('business_id', $businessId)
|
||||
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
|
||||
->groupBy(DB::raw('DATE(incomeDate)'));
|
||||
|
||||
$this->applyDateFilter($incomeQuery, $duration, 'incomeDate', $request->from_date, $request->to_date);
|
||||
$dailyIncomes = $incomeQuery->get();
|
||||
|
||||
$income_datas = $dailyIncomes->map(fn($income) => (object)[
|
||||
'type' => 'Income',
|
||||
'date' => $income->date,
|
||||
'total_incomes' => $income->total_incomes,
|
||||
]);
|
||||
|
||||
$mergedIncomeSaleData = collect();
|
||||
$allDates = $dailySales->pluck('date')
|
||||
->merge($dailyIncomes->pluck('date'))
|
||||
->unique()
|
||||
->sort();
|
||||
|
||||
foreach ($allDates as $date) {
|
||||
if ($income = $income_datas->firstWhere('date', $date)) {
|
||||
$mergedIncomeSaleData->push($income);
|
||||
}
|
||||
if ($sale = $sale_datas->firstWhere('date', $date)) {
|
||||
$mergedIncomeSaleData->push($sale);
|
||||
}
|
||||
}
|
||||
|
||||
$dailyPayrolls = collect();
|
||||
|
||||
if (moduleCheck('HrmAddon')) {
|
||||
$payrollQuery = DB::table('payrolls')
|
||||
->select(
|
||||
DB::raw('DATE(date) as date'),
|
||||
DB::raw('SUM(amount) as total_payrolls')
|
||||
)
|
||||
->where('business_id', $businessId)
|
||||
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
|
||||
->groupBy(DB::raw('DATE(date)'));
|
||||
|
||||
$this->applyDateFilter($payrollQuery, $duration, 'date', $request->from_date, $request->to_date);
|
||||
$dailyPayrolls = $payrollQuery->get();
|
||||
}
|
||||
|
||||
$expenseQuery = DB::table('expenses')
|
||||
->select(
|
||||
DB::raw('DATE(expenseDate) as date'),
|
||||
DB::raw('SUM(amount) as total_expenses_only')
|
||||
)
|
||||
->where('business_id', $businessId)
|
||||
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
|
||||
->groupBy(DB::raw('DATE(expenseDate)'));
|
||||
|
||||
$this->applyDateFilter($expenseQuery, $duration, 'expenseDate', $request->from_date, $request->to_date);
|
||||
$dailyExpenses = $expenseQuery->get();
|
||||
|
||||
$mergedExpenseData = collect();
|
||||
$allExpenseDates = $dailyExpenses->pluck('date')
|
||||
->merge($dailyPayrolls->pluck('date'))
|
||||
->unique()
|
||||
->sort();
|
||||
|
||||
foreach ($allExpenseDates as $date) {
|
||||
if ($expense = $dailyExpenses->firstWhere('date', $date)) {
|
||||
$mergedExpenseData->push((object)[
|
||||
'type' => 'Expense',
|
||||
'date' => $date,
|
||||
'total_expenses' => $expense->total_expenses_only,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($payroll = $dailyPayrolls->firstWhere('date', $date)) {
|
||||
$mergedExpenseData->push((object)[
|
||||
'type' => 'Payroll',
|
||||
'date' => $date,
|
||||
'total_expenses' => $payroll->total_payrolls,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$grossSaleProfit = $sale_datas->sum('total_sales');
|
||||
$grossIncomeProfit = $income_datas->sum('total_incomes') + $sale_datas->sum('total_incomes');
|
||||
$totalExpenses = $mergedExpenseData->sum('total_expenses');
|
||||
$netProfit = $grossIncomeProfit - $totalExpenses;
|
||||
|
||||
$allTimeIncomes = DB::table('incomes')->where('business_id', $businessId)
|
||||
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
|
||||
->sum('amount');
|
||||
|
||||
$allTimeSaleProfit = DB::table('sales')->where('business_id', $businessId)
|
||||
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
|
||||
->sum('lossProfit');
|
||||
|
||||
$allTimePayrolls = moduleCheck('HrmAddon')
|
||||
? DB::table('payrolls')->where('business_id', $businessId)
|
||||
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
|
||||
->sum('amount')
|
||||
: 0;
|
||||
|
||||
$allTimeExpensesOnly = DB::table('expenses')->where('business_id', $businessId)
|
||||
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
|
||||
->sum('amount');
|
||||
|
||||
$cardGrossProfit = $allTimeIncomes + $allTimeSaleProfit;
|
||||
$totalCardExpenses = $allTimePayrolls + $allTimeExpensesOnly;
|
||||
$cardNetProfit = $cardGrossProfit - $totalCardExpenses;
|
||||
|
||||
return response()->json([
|
||||
'mergedIncomeSaleData' => $mergedIncomeSaleData->values(),
|
||||
'mergedExpenseData' => $mergedExpenseData->values(),
|
||||
'grossSaleProfit' => $grossSaleProfit,
|
||||
'grossIncomeProfit' => $grossIncomeProfit,
|
||||
'totalExpenses' => $totalExpenses,
|
||||
'netProfit' => $netProfit,
|
||||
'cardGrossProfit' => $cardGrossProfit,
|
||||
'totalCardExpenses' => $totalCardExpenses,
|
||||
'cardNetProfit' => $cardNetProfit,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function cashFlow(Request $request)
|
||||
{
|
||||
$query = Transaction::with([
|
||||
'paymentType:id,name',
|
||||
'sale:id,party_id',
|
||||
'sale.party:id,name',
|
||||
'saleReturn:id,sale_id',
|
||||
'purchase:id,party_id',
|
||||
'purchase.party:id,name',
|
||||
'purchaseReturn:id,purchase_id',
|
||||
'dueCollect:id,party_id',
|
||||
'dueCollect.party:id,name',
|
||||
])
|
||||
->where('business_id', auth()->user()->business_id)
|
||||
->whereIn('type', ['debit', 'credit']);
|
||||
|
||||
$total_cash_in = (clone $query)
|
||||
->where('type', 'credit')
|
||||
->sum('amount');
|
||||
|
||||
$total_cash_out = (clone $query)
|
||||
->where('type', 'debit')
|
||||
->sum('amount');
|
||||
|
||||
$total_running_cash = $total_cash_in - $total_cash_out;
|
||||
|
||||
// Apply date filter
|
||||
$duration = $request->duration ?: 'today';
|
||||
$this->applyDateFilter($query, $duration, 'date', $request->from_date, $request->to_date);
|
||||
$cash_flows = $query->get();
|
||||
|
||||
$firstDate = $cash_flows->first()?->date;
|
||||
|
||||
if ($firstDate) {
|
||||
$opening_balance = (clone $query)
|
||||
->whereDate('date', '<', $firstDate)
|
||||
->selectRaw("SUM(CASE WHEN type='credit' THEN amount ELSE 0 END) - SUM(CASE WHEN type='debit' THEN amount ELSE 0 END) as balance")
|
||||
->value('balance') ?? 0;
|
||||
} else {
|
||||
$opening_balance = 0;
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'cash_in' => $total_cash_in,
|
||||
'cash_out' => $total_cash_out,
|
||||
'running_cash' => $total_running_cash,
|
||||
'initial_running_cash' => $opening_balance,
|
||||
'data' => $cash_flows,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function balanceSheetReport(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$duration = $request->duration ?: 'today';
|
||||
$fromDate = $request->from_date;
|
||||
$toDate = $request->to_date;
|
||||
|
||||
$productQuery = Product::select('id', 'business_id', 'productName', 'product_type', 'created_at')
|
||||
->with(['stocks:id,business_id,product_id,productStock,productPurchasePrice', 'combo_products.stock'])
|
||||
->where('business_id', $businessId);
|
||||
|
||||
$this->applyDateFilter($productQuery, $duration, 'created_at', $fromDate, $toDate);
|
||||
|
||||
$products = $productQuery->get()
|
||||
->map(function ($item) {
|
||||
$item->source = 'product';
|
||||
return $item;
|
||||
});
|
||||
|
||||
$bankQuery = PaymentType::where('business_id', $businessId);
|
||||
|
||||
$this->applyDateFilter($bankQuery, $duration, 'opening_date', $fromDate, $toDate);
|
||||
|
||||
$banks = $bankQuery->get()
|
||||
->map(function ($item) {
|
||||
$item->source = 'bank';
|
||||
return $item;
|
||||
});
|
||||
|
||||
$product_bank_datas = $products->merge($banks);
|
||||
|
||||
$total_stock_value = 0;
|
||||
|
||||
foreach ($products as $product) {
|
||||
// SINGLE / VARIANT
|
||||
if (in_array($product->product_type, ['single', 'variant'])) {
|
||||
foreach ($product->stocks as $stock) {
|
||||
$total_stock_value += $stock->productStock * $stock->productPurchasePrice;
|
||||
}
|
||||
}
|
||||
|
||||
// COMBO
|
||||
if ($product->product_type === 'combo') {
|
||||
foreach ($product->combo_products as $combo) {
|
||||
$childStock = $combo->stock;
|
||||
if ($childStock) {
|
||||
$total_stock_value += ($childStock->productStock / $combo->quantity) * $combo->purchase_price;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$totalBankBalance = $banks->sum('balance');
|
||||
$total_asset = $total_stock_value + $totalBankBalance;
|
||||
|
||||
return response()->json([
|
||||
'asset_datas' => $product_bank_datas,
|
||||
'total_asset' => $total_asset,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function subscriptionReport(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$duration = $request->duration ?: 'today';
|
||||
$fromDate = $request->from_date;
|
||||
$toDate = $request->to_date;
|
||||
|
||||
$subscriptionQuery = PlanSubscribe::with([
|
||||
'plan:id,subscriptionName',
|
||||
'business:id,companyName,business_category_id,pictureUrl',
|
||||
'business.category:id,name',
|
||||
'gateway:id,name'
|
||||
])->where('business_id', $businessId);
|
||||
|
||||
$this->applyDateFilter($subscriptionQuery, $duration, 'created_at', $fromDate, $toDate);
|
||||
|
||||
$subscriptions = $subscriptionQuery->get();
|
||||
|
||||
return response()->json([
|
||||
'data' => $subscriptions,
|
||||
]);
|
||||
}
|
||||
|
||||
public function taxReport(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$duration = $request->duration ?: 'today';
|
||||
$fromDate = $request->from_date;
|
||||
$toDate = $request->to_date;
|
||||
|
||||
$vats = Vat::where('business_id', $businessId)->whereStatus(1)->get();
|
||||
|
||||
//sales
|
||||
$salesQuery = Sale::with('party:id,name,email,phone,type', 'vat:id,name')
|
||||
->where('business_id', $businessId)
|
||||
->where('vat_amount', '>', 0);
|
||||
|
||||
$this->applyDateFilter($salesQuery, $duration, 'created_at', $fromDate, $toDate);
|
||||
|
||||
$sales = $salesQuery->get()->map(function ($item) {
|
||||
$item->source = 'sale'; // append a source field
|
||||
return $item;
|
||||
});
|
||||
|
||||
$salesTotalAmount = $sales->sum('totalAmount');
|
||||
$salesTotalDiscount = $sales->sum('discountAmount');
|
||||
$salesTotalVat = $sales->sum('vat_amount');
|
||||
|
||||
$salesVatTotals = [];
|
||||
foreach ($vats as $vat) {
|
||||
$salesVatTotals[$vat->id] = $sales->where('vat_id', $vat->id)->sum('vat_amount');
|
||||
}
|
||||
|
||||
//purchase
|
||||
$purchaseQuery = Purchase::with('party:id,name,email,phone,type', 'vat:id,name')
|
||||
->where('business_id', $businessId)
|
||||
->where('vat_amount', '>', 0);
|
||||
|
||||
$this->applyDateFilter($purchaseQuery, $duration, 'created_at', $fromDate, $toDate);
|
||||
|
||||
$purchases = $purchaseQuery->get()->map(function ($item) {
|
||||
$item->source = 'purchase'; // append a source field
|
||||
return $item;
|
||||
});
|
||||
|
||||
$purchasesTotalAmount = $purchases->sum('totalAmount');
|
||||
$purchasesTotalDiscount = $purchases->sum('discountAmount');
|
||||
$purchasesTotalVat = $purchases->sum('vat_amount');
|
||||
|
||||
$purchasesVatTotals = [];
|
||||
foreach ($vats as $vat) {
|
||||
$purchasesVatTotals[$vat->id] = $purchases->where('vat_id', $vat->id)->sum('vat_amount');
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'sales' => $sales,
|
||||
'sales_total_amount' => $salesTotalAmount,
|
||||
'sales_total_discount' => $salesTotalDiscount,
|
||||
'sales_total_vat' => $salesTotalVat,
|
||||
'purchases' => $purchases,
|
||||
'purchases_total_amount' => $purchasesTotalAmount,
|
||||
'purchases_total_discount' => $purchasesTotalDiscount,
|
||||
'purchases_total_vat' => $purchasesTotalVat,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function billWiseProfitReport(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$duration = $request->duration ?: 'today';
|
||||
$fromDate = $request->from_date;
|
||||
$toDate = $request->to_date;
|
||||
|
||||
$billQuery = Sale::select('id', 'business_id', 'party_id', 'invoiceNumber', 'saleDate', 'totalAmount', 'lossProfit')
|
||||
->with('party:id,name', 'details:id,sale_id,product_id,price,quantities,productPurchasePrice,lossProfit', 'details.product:id,productName')->where('business_id', $businessId);
|
||||
|
||||
$this->applyDateFilter($billQuery, $duration, 'saleDate', $fromDate, $toDate);
|
||||
$bills = $billQuery->get();
|
||||
|
||||
$total_amount = $bills->sum('totalAmount');
|
||||
$total_bill_profit = $bills
|
||||
->where('lossProfit', '>=', 0)
|
||||
->sum('lossProfit');
|
||||
|
||||
$total_bill_loss = $bills
|
||||
->where('lossProfit', '<', 0)
|
||||
->sum('lossProfit');
|
||||
|
||||
return response()->json([
|
||||
'data' => $bills,
|
||||
'total_amount' => $total_amount,
|
||||
'total_bill_profit' => $total_bill_profit,
|
||||
'total_bill_loss' => $total_bill_loss,
|
||||
]);
|
||||
}
|
||||
|
||||
public function productSaleHistory(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$duration = $request->duration ?: 'today';
|
||||
|
||||
$productQuery = Product::with(['saleDetails', 'purchaseDetails', 'saleDetails.sale', 'stocks', 'combo_products'])
|
||||
->where('business_id', $businessId)
|
||||
->whereHas('saleDetails.sale', function ($sale) use ($duration, $request) {
|
||||
$this->applyDateFilter($sale, $duration, 'saleDate', $request->from_date, $request->to_date);
|
||||
});
|
||||
|
||||
$products = $productQuery->get();
|
||||
|
||||
$total_purchase_qty = $products->sum(function ($product) {
|
||||
return $product->purchaseDetails->sum('quantities');
|
||||
});
|
||||
$total_sale_qty = $products->sum(function ($product) {
|
||||
return $product->saleDetails->sum('quantities');
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'data' => $products,
|
||||
'total_purchase_qty' => $total_purchase_qty,
|
||||
'total_sale_qty' => $total_sale_qty,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function productSaleHistoryDetails(Request $request, $productId)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$duration = $request->duration ?: 'today';
|
||||
$fromDate = $request->from_date;
|
||||
$toDate = $request->to_date;
|
||||
|
||||
$product = Product::select('id', 'business_id', 'productName')
|
||||
->with([
|
||||
'saleDetails' => function ($q) use ($duration, $fromDate, $toDate) {
|
||||
|
||||
$q->whereHas('sale', function ($sale) use ($duration, $fromDate, $toDate) {
|
||||
$this->applyDateFilter($sale, $duration, 'saleDate', $fromDate, $toDate);
|
||||
});
|
||||
|
||||
$q->select('id', 'sale_id', 'product_id', 'quantities', 'price', 'productPurchasePrice')
|
||||
->with([
|
||||
'sale:id,invoiceNumber,saleDate'
|
||||
]);
|
||||
},
|
||||
])->where('business_id', $businessId)->findOrFail($productId);
|
||||
|
||||
$totalQuantities = $product->saleDetails->sum('quantities');
|
||||
$totalSalePrice = $product->saleDetails->sum('price');
|
||||
$totalPurchasePrice = $product->saleDetails->sum('productPurchasePrice');
|
||||
|
||||
return response()->json([
|
||||
'data' => $product,
|
||||
'total_quantities' => $totalQuantities,
|
||||
'total_sale_price' => $totalSalePrice,
|
||||
'total_purchase_price' => $totalPurchasePrice,
|
||||
]);
|
||||
}
|
||||
|
||||
public function productPurchaseHistory(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$duration = $request->duration ?: 'today';
|
||||
|
||||
$productQuery = Product::with(['saleDetails', 'purchaseDetails', 'purchaseDetails.purchase', 'stocks', 'combo_products'])
|
||||
->where('business_id', $businessId)
|
||||
->whereHas('purchaseDetails.purchase', function ($purchase) use ($duration, $request) {
|
||||
$this->applyDateFilter($purchase, $duration, 'purchaseDate', $request->from_date, $request->to_date);
|
||||
});
|
||||
|
||||
$products = $productQuery->get();
|
||||
|
||||
$total_purchase_qty = $products->sum(function ($product) {
|
||||
return $product->purchaseDetails->sum('quantities');
|
||||
});
|
||||
$total_sale_qty = $products->sum(function ($product) {
|
||||
return $product->saleDetails->sum('quantities');
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'data' => $products,
|
||||
'total_purchase_qty' => $total_purchase_qty,
|
||||
'total_sale_qty' => $total_sale_qty,
|
||||
]);
|
||||
}
|
||||
|
||||
public function productPurchaseHistoryDetails(Request $request, $productId)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
$duration = $request->duration ?: 'today';
|
||||
$fromDate = $request->from_date;
|
||||
$toDate = $request->to_date;
|
||||
|
||||
$product = Product::select('id', 'business_id', 'productName')
|
||||
->with([
|
||||
'purchaseDetails' => function ($q) use ($duration, $fromDate, $toDate) {
|
||||
|
||||
$q->whereHas('purchase', function ($purchase) use ($duration, $fromDate, $toDate) {
|
||||
$this->applyDateFilter($purchase, $duration, 'purchaseDate', $fromDate, $toDate);
|
||||
});
|
||||
|
||||
$q->select('id', 'purchase_id', 'product_id', 'quantities', 'productPurchasePrice')
|
||||
->with([
|
||||
'purchase:id,invoiceNumber,purchaseDate'
|
||||
]);
|
||||
},
|
||||
])->where('business_id', $businessId)->findOrFail($productId);
|
||||
|
||||
$totalQuantities = $product->purchaseDetails->sum('quantities');
|
||||
$totalPurchasePrice = $product->purchaseDetails->sum('productPurchasePrice');
|
||||
|
||||
return response()->json([
|
||||
'data' => $product,
|
||||
'total_quantities' => $totalQuantities,
|
||||
'total_purchase_price' => $totalPurchasePrice,
|
||||
]);
|
||||
}
|
||||
}
|
||||
561
app/Http/Controllers/Api/AcnooSaleController.php
Normal file
561
app/Http/Controllers/Api/AcnooSaleController.php
Normal file
@@ -0,0 +1,561 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Events\MultiPaymentProcessed;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\Product;
|
||||
use App\Models\Sale;
|
||||
use App\Models\Party;
|
||||
use App\Models\Stock;
|
||||
use App\Events\SaleSms;
|
||||
use App\Models\SaleDetails;
|
||||
use App\Helpers\HasUploader;
|
||||
use App\Models\Transaction;
|
||||
use App\Traits\DateFilterTrait;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class AcnooSaleController extends Controller
|
||||
{
|
||||
use HasUploader;
|
||||
Use DateFilterTrait;
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
// Build the query with all eager loads
|
||||
$query = Sale::with([
|
||||
'user:id,name,role',
|
||||
'party:id,name,email,phone,type,address',
|
||||
'details',
|
||||
'details.stock:id,batch_no,productStock',
|
||||
'details.product:id,productName,category_id,productCode,productPurchasePrice,productStock,product_type',
|
||||
'details.product.category:id,categoryName',
|
||||
'saleReturns.details',
|
||||
'vat:id,name,rate',
|
||||
'branch:id,name,phone,address',
|
||||
'transactions:id,platform,transaction_type,amount,date,invoice_no,reference_id,payment_type_id,meta',
|
||||
'transactions.paymentType:id,name,meta'
|
||||
])->where('business_id', $business_id);
|
||||
|
||||
// Filter returned sales
|
||||
$query->when(request('returned-sales') == "true", function ($query) {
|
||||
$query->whereHas('saleReturns');
|
||||
});
|
||||
|
||||
// Branch filter
|
||||
if ($request->filled('branch_id')) {
|
||||
$query->where('branch_id', $request->branch_id);
|
||||
}
|
||||
|
||||
// Apply date filter
|
||||
if(request('duration')){
|
||||
$this->applyDateFilter($query, request('duration'), 'saleDate', request('from_date'), request('to_date'));
|
||||
}
|
||||
|
||||
// Search filter
|
||||
if ($request->filled('search')) {
|
||||
$query->where(function ($q) use ($request) {
|
||||
$q->where('invoiceNumber', 'like', "%{$request->search}%")
|
||||
->orWhere('paymentType', 'like', "%{$request->search}%")
|
||||
->orWhereHas('party', fn($q) => $q->where('name', 'like', "%{$request->search}%"))
|
||||
->orWhereHas('payment_type', fn($q) => $q->where('name', 'like', "%{$request->search}%"))
|
||||
->orWhereHas('branch', fn($q) => $q->where('name', 'like', "%{$request->search}%"));
|
||||
});
|
||||
}
|
||||
|
||||
$data = $query->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'total_amount' => $data->sum('totalAmount'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(string $id)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$sale = Sale::with(['user:id,name,role', 'party:id,name,email,phone,type,address', 'details', 'details.stock:id,batch_no,productStock', 'details.product:id,productName,category_id,productCode,productPurchasePrice,productStock,product_type', 'details.product.category:id,categoryName', 'saleReturns.details', 'vat:id,name,rate', 'branch:id,name,phone,address', 'transactions:id,platform,transaction_type,amount,date,invoice_no,reference_id,payment_type_id,meta', 'transactions.paymentType:id,name,meta'])
|
||||
->where('business_id', $business_id)
|
||||
->where('id', $id)
|
||||
->first();
|
||||
|
||||
if (!$sale) {
|
||||
return response()->json([
|
||||
'message' => __('Sale not found.'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Sale fetched successfully.'),
|
||||
'data' => $sale,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'party_id' => 'nullable|exists:parties,id',
|
||||
'vat_id' => 'nullable|exists:vats,id',
|
||||
'products' => 'required',
|
||||
'saleDate' => 'nullable|date',
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg',
|
||||
'rounding_option' => 'nullable|in:none,round_up,nearest_whole_number,nearest_0.05,nearest_0.1,nearest_0.5',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$business_id = auth()->user()->business_id;
|
||||
$request_products = json_decode($request->products, true);
|
||||
$payments = json_decode($request->payments, true);
|
||||
|
||||
$paidAmount = collect($payments)
|
||||
->reject(fn($p) => strtolower($p['type'] ?? '') === 'cheque')
|
||||
->sum(fn($p) => $p['amount'] ?? 0);
|
||||
|
||||
if ($request->dueAmount > 0) {
|
||||
$allowDueForGuest = product_setting()?->modules['allow_due_sale'] ?? false;
|
||||
|
||||
// No party
|
||||
if (!$request->party_id) {
|
||||
if (!$allowDueForGuest) {
|
||||
return response()->json([
|
||||
'message' => __('You have not allowed guest sales, so you cannot sell on due for a walking customer.')
|
||||
], 400);
|
||||
}
|
||||
} else {
|
||||
// Party exist
|
||||
$party = Party::findOrFail($request->party_id);
|
||||
$newDue = $party->due + $request->dueAmount;
|
||||
|
||||
// Check credit limit
|
||||
if ($party->credit_limit > 0 && $newDue > $party->credit_limit) {
|
||||
return response()->json([
|
||||
'message' => __('Sale cannot be created. Party due will exceed credit limit!')
|
||||
], 400);
|
||||
}
|
||||
|
||||
$party->update(['due' => $newDue]);
|
||||
}
|
||||
}
|
||||
|
||||
updateBalance($paidAmount, 'increment');
|
||||
|
||||
$sale = Sale::create($request->except('image', 'isPaid', 'lossProfit', 'payment_type_id') + [
|
||||
'user_id' => auth()->id(),
|
||||
'business_id' => $business_id,
|
||||
'isPaid' => filter_var($request->isPaid, FILTER_VALIDATE_BOOLEAN) ? 1 : 0,
|
||||
'image' => $request->image ? $this->upload($request, 'image') : null,
|
||||
'meta' => [
|
||||
'note' => $request->note,
|
||||
],
|
||||
]);
|
||||
|
||||
$subtotal = 0;
|
||||
$totalPurchaseAmount = 0;
|
||||
|
||||
foreach ($request_products as $productData) {
|
||||
$productDiscount = $productData['discount'] ?? 0;
|
||||
|
||||
$product = Product::findOrFail($productData['product_id']);
|
||||
$qty = $productData['quantities'] ?? 1;
|
||||
$price = $productData['price'] ?? 0;
|
||||
|
||||
$lineSubtotal = ($price * $qty) - $productDiscount;
|
||||
$subtotal += $lineSubtotal;
|
||||
|
||||
// Combo Products
|
||||
if ($product->product_type == 'combo') {
|
||||
$comboTotalPurchase = 0;
|
||||
|
||||
foreach ($product->combo_products as $comboItem) {
|
||||
$stock = Stock::findOrFail($comboItem->stock_id);
|
||||
$requiredQty = $comboItem->quantity * $qty;
|
||||
|
||||
if ($stock->productStock < $requiredQty) {
|
||||
return response()->json([
|
||||
'message' => "Combo item '{$productData['product_name']}' (Batch: {$stock->batch_no}) not available. Available stock: {$stock->productStock}"
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Decrement stock
|
||||
$stock->decrement('productStock', $requiredQty);
|
||||
|
||||
// Combo purchase total
|
||||
$comboTotalPurchase += $stock->productPurchasePrice * $requiredQty;
|
||||
}
|
||||
|
||||
$totalPurchaseAmount += $comboTotalPurchase;
|
||||
|
||||
// Store combo in SaleDetails
|
||||
$saleDetails[] = [
|
||||
'sale_id' => $sale->id,
|
||||
'stock_id' => null,
|
||||
'product_id' => $product->id,
|
||||
'price' => $price,
|
||||
'discount' => $productDiscount,
|
||||
'lossProfit' => $lineSubtotal - $comboTotalPurchase,
|
||||
'quantities' => $qty,
|
||||
'productPurchasePrice' => $comboTotalPurchase ?? 0,
|
||||
'expire_date' => null,
|
||||
'warranty_guarantee_info' => $product->warranty_guarantee_info
|
||||
? json_encode($product->warranty_guarantee_info)
|
||||
: null,
|
||||
];
|
||||
} // Single / Variant Products
|
||||
else {
|
||||
$stock = Stock::findOrFail($productData['stock_id']);
|
||||
|
||||
if ($stock->productStock < $qty) {
|
||||
return response()->json([
|
||||
'message' => "Stock not available for product: {$productData['product_name']}. Available: {$stock->productStock}"
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Decrement stock
|
||||
$stock->decrement('productStock', $qty);
|
||||
$purchasePrice = $stock->productPurchasePrice ?? 0;
|
||||
|
||||
// Calculate profit/loss
|
||||
$totalPurchaseAmount += ($purchasePrice * $qty);
|
||||
|
||||
$saleDetails[] = [
|
||||
'sale_id' => $sale->id,
|
||||
'stock_id' => $stock->id,
|
||||
'product_id' => $product->id,
|
||||
'price' => $price,
|
||||
'discount' => $productDiscount,
|
||||
'lossProfit' => $lineSubtotal - ($purchasePrice * $qty),
|
||||
'quantities' => $qty,
|
||||
'productPurchasePrice' => $purchasePrice,
|
||||
'expire_date' => $stock->expire_date ?? null,
|
||||
'warranty_guarantee_info' => $product->warranty_guarantee_info
|
||||
? json_encode($product->warranty_guarantee_info)
|
||||
: null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
SaleDetails::insert($saleDetails);
|
||||
|
||||
$saleLossProfit = ($subtotal - $totalPurchaseAmount) - ($request->discountAmount ?? 0);
|
||||
|
||||
$sale->update([
|
||||
'lossProfit' => $saleLossProfit,
|
||||
]);
|
||||
|
||||
// MultiPaymentProcessed Event
|
||||
event(new MultiPaymentProcessed(
|
||||
$payments,
|
||||
$sale->id,
|
||||
'sale',
|
||||
$paidAmount,
|
||||
$request->party_id ?? null
|
||||
));
|
||||
|
||||
// Send SMS
|
||||
event(new SaleSms($sale));
|
||||
|
||||
DB::commit();
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $sale->load('user:id,name,role', 'party:id,name,email,phone,type,address', 'details', 'details.stock:id,batch_no', 'details.product:id,productName,category_id,product_type', 'details.product.category:id,categoryName', 'saleReturns.details', 'vat:id,name,rate', 'payment_type:id,name', 'branch:id,name,phone,address'),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'party_id' => 'nullable|exists:parties,id',
|
||||
'vat_id' => 'nullable|exists:vats,id',
|
||||
'products' => 'required',
|
||||
'saleDate' => 'nullable|date',
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg',
|
||||
'rounding_option' => 'nullable|in:none,round_up,nearest_whole_number,nearest_0.05,nearest_0.1,nearest_0.5',
|
||||
]);
|
||||
|
||||
$sale = Sale::findOrFail($id);
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
if ($sale->load('saleReturns')->saleReturns->count() > 0) {
|
||||
return response()->json([
|
||||
'message' => __("You cannot update this sale because it has already been returned.")
|
||||
], 400);
|
||||
}
|
||||
|
||||
$request_products = json_decode($request->products, true);
|
||||
$payments = json_decode($request->payments, true);
|
||||
$prevDetails = SaleDetails::where('sale_id', $sale->id)->get();
|
||||
|
||||
// Rollback previous stock
|
||||
foreach ($prevDetails as $prevItem) {
|
||||
if ($prevItem->stock_id) {
|
||||
$stock = Stock::findOrFail($prevItem->stock_id);
|
||||
$stock->increment('productStock', $prevItem->quantities);
|
||||
}
|
||||
}
|
||||
|
||||
$prevDetails->each->delete();
|
||||
|
||||
$saleDetails = [];
|
||||
$totalLossProfit = 0;
|
||||
$totalProductWiseDiscount = 0;
|
||||
|
||||
foreach ($request_products as $productData) {
|
||||
$product = Product::findOrFail($productData['product_id']);
|
||||
$qty = $productData['quantities'] ?? 1;
|
||||
$price = $productData['price'] ?? 0;
|
||||
|
||||
$productDiscount = $productData['discount'] ?? 0;
|
||||
$totalProductWiseDiscount += $productDiscount;
|
||||
|
||||
$unitSalePrice = $price;
|
||||
$unitDiscount = $productDiscount / max($qty, 1);
|
||||
$finalSalePricePerUnit = $unitSalePrice - $unitDiscount;
|
||||
|
||||
// Combo product
|
||||
if ($product->product_type == 'combo') {
|
||||
foreach ($product->combo_products as $comboItem) {
|
||||
$stock = Stock::findOrFail($comboItem->stock_id);
|
||||
$requiredQty = $comboItem->quantity * $qty;
|
||||
|
||||
if ($stock->productStock < $requiredQty) {
|
||||
return response()->json([
|
||||
'message' => "Combo item '{$productData['product_name']}' (Batch: {$stock->batch_no}) not available. Available stock: {$stock->productStock}"
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Decrement stock
|
||||
$stock->decrement('productStock', $requiredQty);
|
||||
|
||||
// Total combo purchase
|
||||
$comboPurchaseTotal += ($stock->productPurchasePrice * $requiredQty); // 🔥 changed
|
||||
}
|
||||
|
||||
// purchase price per unit of combo
|
||||
$purchaseUnit = $comboPurchaseTotal / max($qty, 1);
|
||||
|
||||
// correct lossProfit
|
||||
$lossProfit = ($finalSalePricePerUnit - $purchaseUnit) * $qty;
|
||||
$totalLossProfit += $lossProfit;
|
||||
|
||||
$saleDetails[] = [
|
||||
'sale_id' => $sale->id,
|
||||
'stock_id' => null,
|
||||
'product_id' => $product->id,
|
||||
'price' => $price,
|
||||
'discount' => $productDiscount,
|
||||
'lossProfit' => $lossProfit,
|
||||
'quantities' => $qty,
|
||||
'productPurchasePrice' => $purchaseUnit,
|
||||
'expire_date' => null,
|
||||
'warranty_guarantee_info' => $product->warranty_guarantee_info
|
||||
? json_encode($product->warranty_guarantee_info)
|
||||
: null,
|
||||
];
|
||||
} // Single / Variant product
|
||||
else {
|
||||
$stock = Stock::findOrFail($productData['stock_id']);
|
||||
if ($stock->productStock < $qty) {
|
||||
return response()->json([
|
||||
'message' => "Stock not available for product: {$productData['product_name']}. Available: {$stock->productStock}"
|
||||
], 400);
|
||||
}
|
||||
|
||||
$stock->decrement('productStock', $qty);
|
||||
|
||||
$purchaseUnit = $stock->productPurchasePrice ?? 0;
|
||||
$lossProfit = ($finalSalePricePerUnit - $purchaseUnit) * $qty;
|
||||
$totalLossProfit += $lossProfit;
|
||||
|
||||
$saleDetails[] = [
|
||||
'sale_id' => $sale->id,
|
||||
'stock_id' => $stock->id,
|
||||
'product_id' => $product->id,
|
||||
'price' => $price,
|
||||
'discount' => $productDiscount,
|
||||
'lossProfit' => $lossProfit,
|
||||
'quantities' => $qty,
|
||||
'productPurchasePrice' => $purchaseUnit,
|
||||
'expire_date' => $stock->expire_date ?? null,
|
||||
'warranty_guarantee_info' => $product->warranty_guarantee_info ? json_encode($product->warranty_guarantee_info) : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
SaleDetails::insert($saleDetails);
|
||||
|
||||
// Check if any old transaction type = deposit
|
||||
$hasDeposit = Transaction::where('business_id', $business_id)
|
||||
->where('reference_id', $sale->id)
|
||||
->where('platform', 'sale')
|
||||
->where('type', 'deposit')
|
||||
->exists();
|
||||
|
||||
$paidAmount = collect($payments)
|
||||
->reject(function ($p) use ($hasDeposit) {
|
||||
// exclude cheque only if not deposit
|
||||
return !$hasDeposit && strtolower($p['type'] ?? '') === 'cheque';
|
||||
})
|
||||
->sum(fn($p) => $p['amount'] ?? 0);
|
||||
|
||||
$dueAmount = max($request->totalAmount - $paidAmount, 0);
|
||||
|
||||
|
||||
// Handle party due
|
||||
if ($sale->dueAmount || $dueAmount) {
|
||||
$party = Party::findOrFail($request->party_id);
|
||||
|
||||
$newDue = $request->party_id == $sale->party_id
|
||||
? (($party->due - $sale->dueAmount) + $dueAmount)
|
||||
: ($party->due + $dueAmount);
|
||||
|
||||
if ($party->credit_limit > 0 && $newDue > $party->credit_limit) {
|
||||
return response()->json([
|
||||
'message' => __("Cannot update sale. Party due will exceed credit limit!")
|
||||
], 400);
|
||||
}
|
||||
|
||||
$party->update(['due' => $newDue]);
|
||||
|
||||
if ($request->party_id != $sale->party_id) {
|
||||
$prev_party = Party::findOrFail($sale->party_id);
|
||||
$prev_party->update([
|
||||
'due' => $prev_party->due - $sale->dueAmount
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust business balance
|
||||
$balanceDiff = $paidAmount - $sale->paidAmount;
|
||||
updateBalance($balanceDiff, 'increment');
|
||||
|
||||
$sale->update($request->except('image', 'isPaid', 'paidAmount', 'paidAmount', 'dueAmount') + [
|
||||
'user_id' => auth()->id(),
|
||||
'isPaid' => filter_var($request->isPaid, FILTER_VALIDATE_BOOLEAN) ? 1 : 0,
|
||||
'lossProfit' => $totalLossProfit - ($request->discountAmount ?? 0) - $totalProductWiseDiscount,
|
||||
'image' => $request->image ? $this->upload($request, 'image', $sale->image) : $sale->image,
|
||||
'paidAmount' => $paidAmount > $request->totalAmount ? $request->totalAmount : $paidAmount,
|
||||
'dueAmount' => $dueAmount,
|
||||
'meta' => [
|
||||
'note' => $request->note,
|
||||
],
|
||||
]);
|
||||
|
||||
// Multiple Payment Process
|
||||
$oldTransactions = Transaction::where('business_id', $business_id)
|
||||
->where('reference_id', $sale->id)
|
||||
->where('platform', 'sale')
|
||||
->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->decrement('balance', $old->amount);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'wallet_payment':
|
||||
if ($request->party_id) {
|
||||
$party = Party::find($request->party_id);
|
||||
if ($party) {
|
||||
$party->increment('wallet', $old->amount);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'cash_payment':
|
||||
case 'cheque_payment':
|
||||
// nothing to revert for cash/cheque
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete old transactions
|
||||
Transaction::where('business_id', $business_id)
|
||||
->where('reference_id', $sale->id)
|
||||
->where('platform', 'sale')
|
||||
->delete();
|
||||
|
||||
// Process new payments
|
||||
event(new MultiPaymentProcessed(
|
||||
$payments ?? [],
|
||||
$sale->id,
|
||||
'sale',
|
||||
$paidAmount,
|
||||
$request->party_id ?? null,
|
||||
));
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Sale updated successfully.'),
|
||||
'data' => $sale->load(
|
||||
'details',
|
||||
'details.product:id,productName,product_type',
|
||||
'details.stock:id,batch_no',
|
||||
'party:id,name,email,phone,address',
|
||||
'vat:id,name,rate',
|
||||
'branch:id,name,phone,address'
|
||||
),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json([
|
||||
'message' => 'Something went wrong.',
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Sale $sale)
|
||||
{
|
||||
foreach ($sale->details as $item) {
|
||||
Stock::findOrFail($item->stock_id)->increment('productStock', $item->quantities);
|
||||
}
|
||||
|
||||
if ($sale->dueAmount) {
|
||||
$party = Party::findOrFail($sale->party_id);
|
||||
$party->update([
|
||||
'due' => $party->due - $sale->dueAmount
|
||||
]);
|
||||
}
|
||||
|
||||
updateBalance($sale->paidAmount, 'decrement');
|
||||
|
||||
if (file_exists($sale->image)) {
|
||||
Storage::delete($sale->image);
|
||||
}
|
||||
|
||||
$sale->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
18
app/Http/Controllers/Api/AcnooSubscribesController.php
Normal file
18
app/Http/Controllers/Api/AcnooSubscribesController.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\PlanSubscribe;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class AcnooSubscribesController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$subscribes = PlanSubscribe::where('user_id', auth()->id())->latest()->get();
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $subscribes
|
||||
]);
|
||||
}
|
||||
}
|
||||
18
app/Http/Controllers/Api/AcnooSubscriptionsController.php
Normal file
18
app/Http/Controllers/Api/AcnooSubscriptionsController.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Plan;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class AcnooSubscriptionsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$plans = Plan::whereStatus(1)->latest()->get();
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $plans,
|
||||
]);
|
||||
}
|
||||
}
|
||||
18
app/Http/Controllers/Api/AcnooTermsController.php
Normal file
18
app/Http/Controllers/Api/AcnooTermsController.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Option;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class AcnooTermsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$term = Option::where('key', 'term')->first()->value;
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $term ?? 'Terms & Conditions.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
103
app/Http/Controllers/Api/AcnooUserController.php
Normal file
103
app/Http/Controllers/Api/AcnooUserController.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class AcnooUserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
$data = User::with('branch:id,name')
|
||||
->where('business_id', $user->business_id)
|
||||
->when($user->role == 'staff', function ($q) use ($user) {
|
||||
$q->where('branch_id', $user->branch_id);
|
||||
})
|
||||
->when($user->branch_id || $user->active_branch_id, function ($q) use ($user) {
|
||||
$q->where('branch_id', $user->branch_id ?? $user->active_branch_id);
|
||||
})
|
||||
->where('id', '!=', auth()->id())
|
||||
->where('role', 'staff')
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|max:30',
|
||||
'password' => 'required|min:4|max:15',
|
||||
'email' => 'required|email|unique:users,email',
|
||||
'branch_id' => 'nullable|exists:branches,id',
|
||||
]);
|
||||
|
||||
$data = User::create([
|
||||
'role' => 'staff',
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'visibility' => $request->visibility,
|
||||
'password' => Hash::make($request->password),
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'branch_id' => $request->branch_id ?? auth()->user()->branch_id ?? auth()->user()->active_branch_id,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, User $user)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|max:30',
|
||||
'password' => 'nullable|min:4|max:15',
|
||||
'branch_id' => 'nullable|exists:branches,id',
|
||||
'email' => 'required|email|unique:users,email,' . $user->id,
|
||||
]);
|
||||
|
||||
$user->update([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'branch_id' => $request->branch_id,
|
||||
'visibility' => $request->visibility,
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'password' => $request->password ? Hash::make($request->password) : $user->password,
|
||||
'branch_id' => $request->branch_id ?? auth()->user()->branch_id ?? auth()->user()->active_branch_id,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
$user->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
162
app/Http/Controllers/Api/AcnooVatController.php
Normal file
162
app/Http/Controllers/Api/AcnooVatController.php
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Business;
|
||||
use App\Models\Vat;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AcnooVatController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$vats = Vat::where('business_id', auth()->user()->business_id)
|
||||
->when(request('type') == 'single', function ($query) {
|
||||
$query->whereNull('sub_vat');
|
||||
})
|
||||
->when(request('type') == 'group', function ($query) {
|
||||
$query->whereNotNull('sub_vat');
|
||||
})
|
||||
->when(request('status'), function ($query) {
|
||||
$query->where('status', request('status') == 'active' ? 1 : 0);
|
||||
})
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Data fetched successfully.',
|
||||
'data' => $vats,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'vat_ids' => 'required_if:rate,null',
|
||||
'rate' => 'required_if:rate,null|numeric',
|
||||
]);
|
||||
|
||||
if ($request->rate && !$request->vat_ids) {
|
||||
|
||||
$vat = Vat::create($request->all() + [
|
||||
'business_id' => auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
} elseif (!$request->rate && $request->vat_ids) {
|
||||
|
||||
$vats = Vat::whereIn('id', $request->vat_ids)->select('id', 'name', 'rate')->get();
|
||||
|
||||
$tax_rate = 0;
|
||||
$sub_vats = [];
|
||||
|
||||
foreach ($vats as $vat) {
|
||||
$sub_vats[] = [
|
||||
'id' => $vat->id,
|
||||
'name' => $vat->name,
|
||||
'rate' => $vat->rate,
|
||||
];
|
||||
$tax_rate += $vat->rate;
|
||||
}
|
||||
|
||||
$vat = Vat::create([
|
||||
'rate' => $tax_rate,
|
||||
'sub_vat' => $sub_vats,
|
||||
'name' => $request->name,
|
||||
'business_id' => auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => 'Invalid data format.',
|
||||
], 406);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Data created successfully.',
|
||||
'data' => $vat,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, Vat $vat)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'vat_ids' => 'required_if:rate,null',
|
||||
'rate' => 'required_if:rate,null|numeric',
|
||||
]);
|
||||
|
||||
if ($request->rate && !$request->vat_ids) {
|
||||
|
||||
$vat->update($request->all());
|
||||
|
||||
$vatGroupExist = Vat::where('sub_vat', 'LIKE', '%"id":' . $vat->id . '%')->get();
|
||||
foreach ($vatGroupExist as $group) {
|
||||
$subVats = collect($group->sub_vat)->map(function ($subVat) use ($vat) {
|
||||
if ($subVat['id'] == $vat->id) {
|
||||
$subVat['rate'] = $vat->rate;
|
||||
$subVat['name'] = $vat->name;
|
||||
}
|
||||
return $subVat;
|
||||
});
|
||||
|
||||
$group->update([
|
||||
'rate' => $subVats->sum('rate'),
|
||||
'sub_vat' => $subVats->toArray(),
|
||||
]);
|
||||
}
|
||||
|
||||
} elseif (!$request->rate && $request->vat_ids) {
|
||||
|
||||
$vats = Vat::whereIn('id', $request->vat_ids)->select('id', 'name', 'rate')->get();
|
||||
|
||||
$tax_rate = 0;
|
||||
$sub_vats = [];
|
||||
|
||||
foreach ($vats as $single_tax) {
|
||||
$sub_vats[] = [
|
||||
'id' => $single_tax->id,
|
||||
'name' => $single_tax->name,
|
||||
'rate' => $single_tax->rate,
|
||||
];
|
||||
$tax_rate += $single_tax->rate;
|
||||
}
|
||||
|
||||
$vat->update([
|
||||
'rate' => $tax_rate,
|
||||
'sub_vat' => $sub_vats,
|
||||
'name' => $request->name,
|
||||
'status' => $request->status ?? $vat->status,
|
||||
]);
|
||||
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => 'Invalid data format.',
|
||||
], 406);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Data updated successfully.',
|
||||
'data' => $vat,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function destroy(Vat $vat)
|
||||
{
|
||||
// When sub_vat is null, check if the VAT exists in any other VAT's sub_vat
|
||||
if (is_null($vat->sub_vat) && Vat::where('sub_vat', 'LIKE', '%"id":' . $vat->id . '%')->exists()) {
|
||||
return response()->json([
|
||||
'message' => 'Cannot delete. This VAT is part of a VAT group.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$vat->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Data deleted successfully',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Auth;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Mail\PasswordReset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class AcnooForgotPasswordController extends Controller
|
||||
{
|
||||
public function sendResetCode(Request $request) : JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'email' => 'required|email|exists:users,email'
|
||||
]);
|
||||
|
||||
$expire = now()->addHour();
|
||||
$code = random_int(100000,999999);
|
||||
$user = User::where('email',$request->email)->first();
|
||||
$user->update(['remember_token' => $code, 'email_verified_at' => $expire]);
|
||||
|
||||
$data = [
|
||||
'code' => $code
|
||||
];
|
||||
|
||||
try {
|
||||
if (env('QUEUE_MAIL')) {
|
||||
Mail::to($request->email)->queue(new PasswordReset($data));
|
||||
} else {
|
||||
Mail::to($request->email)->send(new PasswordReset($data));
|
||||
}
|
||||
return response()->json([
|
||||
'message' => 'Password reset code has been sent to your email.',
|
||||
]);
|
||||
|
||||
} catch (\Exception $exception){
|
||||
return response()->json([
|
||||
'message' => $exception->getMessage(),
|
||||
], 422);
|
||||
}
|
||||
}
|
||||
|
||||
public function verifyResetCode(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'code' => 'required|integer',
|
||||
'email' => 'required|exists:users,email',
|
||||
]);
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
if ($user->remember_token == $request->code) {
|
||||
if ($user->email_verified_at > now()) {
|
||||
return response()->json([
|
||||
'message' => __('The code has been verified.')
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
'error' => __('The verification code has expired.')
|
||||
], 400);
|
||||
}
|
||||
} else {
|
||||
return response()->json([
|
||||
'error' => __('Invalid Code.')
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
|
||||
public function resetPassword(Request $request) : JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'email' => 'required|exists:users,email',
|
||||
'password' => ['required', 'min:4'],
|
||||
]);
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
$user->update([
|
||||
'password' => bcrypt($request->password),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Your password has been changed!',
|
||||
]);
|
||||
}
|
||||
}
|
||||
323
app/Http/Controllers/Api/Auth/AuthController.php
Normal file
323
app/Http/Controllers/Api/Auth/AuthController.php
Normal file
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Auth;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Branch;
|
||||
use App\Models\Currency;
|
||||
use App\Mail\WelcomeMail;
|
||||
use App\Models\UserCurrency;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Laravel\Sanctum\NewAccessToken;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use App\Http\Controllers\Auth\RegisteredUserController;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
public function otpSettings()
|
||||
{
|
||||
$otp_settings = get_option('email-varification');
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Data fetched successfully.',
|
||||
'data' => $otp_settings
|
||||
]);
|
||||
}
|
||||
|
||||
public function signUp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'password' => 'required|min:6|max:100',
|
||||
'email' => 'required|email',
|
||||
]);
|
||||
|
||||
$code = random_int(100000, 999999);
|
||||
$otpController = new RegisteredUserController();
|
||||
$visibility_time = $otpController->getOtpTimeInSeconds();
|
||||
$expire = now()->addSeconds($visibility_time);
|
||||
|
||||
$data = [
|
||||
'code' => $code,
|
||||
'name' => $request->name,
|
||||
];
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
if ($user && $user->business_id) {
|
||||
return response()->json([
|
||||
'message' => 'This email is already exists.',
|
||||
], 406);
|
||||
}
|
||||
|
||||
$otp_settings = get_option('email-varification');
|
||||
$verify_email = ($otp_settings['otp_status'] ?? false) && $otp_settings['otp_status'] == 'on' ? 1 : 0;
|
||||
|
||||
if ($verify_email) {
|
||||
if (env('MAIL_USERNAME')) {
|
||||
if (env('QUEUE_MAIL')) {
|
||||
Mail::to($request->email)->queue(new WelcomeMail($data));
|
||||
} else {
|
||||
Mail::to($request->email)->send(new WelcomeMail($data));
|
||||
}
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => __('Mail service is not configured. Please contact your administrator.'),
|
||||
], 406);
|
||||
}
|
||||
}
|
||||
|
||||
$user = User::updateOrCreate(['email' => $request->email], $request->except('password') + [
|
||||
'is_verified' => $verify_email ? 0 : 1,
|
||||
'remember_token' => $verify_email ? $code : NULL,
|
||||
'email_verified_at' => $verify_email ? $expire : NULL,
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => $verify_email ? 'An otp code has been sent to your email. Please check and confirm.' : 'Sign Up completed. Please setup your profile.',
|
||||
'token' => $verify_email ? null : $user->createToken('createToken')->plainTextToken,
|
||||
'data' => $user,
|
||||
]);
|
||||
}
|
||||
|
||||
public function submitOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'email' => 'required|email',
|
||||
'otp' => 'required|min:4|max:15',
|
||||
]);
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json([
|
||||
'status' => 404,
|
||||
'message' => __('User not found'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
if ($user->remember_token == $request->otp) {
|
||||
if ($user->email_verified_at > now()) {
|
||||
|
||||
Auth::login($user);
|
||||
$is_setup = $user->business_id ? true : false;
|
||||
$token = $user->createToken('createToken')->plainTextToken;
|
||||
$accessToken = $user->createToken('createToken');
|
||||
$this->setAccessTokenExpiration($accessToken);
|
||||
|
||||
$user->update([
|
||||
'is_verified' => 1,
|
||||
'remember_token' => NULL,
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
$currency = Currency::select('id', 'name', 'code', 'symbol', 'position')->where('is_default', 1)->first();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Logged In successfully!',
|
||||
'is_setup' => $is_setup,
|
||||
'token' => $token,
|
||||
'currency' => $currency,
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
'error' => __('The verification otp has been expired.')
|
||||
], 400);
|
||||
}
|
||||
} else {
|
||||
return response()->json([
|
||||
'error' => __('Invalid otp.')
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'password' => 'required',
|
||||
'email' => 'required|email',
|
||||
]);
|
||||
|
||||
if (auth()->attempt($request->only('email', 'password'))) {
|
||||
$user = auth()->user();
|
||||
|
||||
$business = $user->business;
|
||||
$branch = Branch::find($user->branch_id);
|
||||
|
||||
if (multibranch_active() && branch_count()) {
|
||||
if ($branch && !$branch->status && $user->branch_id && !$branch->is_main) {
|
||||
|
||||
Auth::logout();
|
||||
return response()->json([
|
||||
'message' => 'This branch is not active, Please contact with manager.',
|
||||
], 406);
|
||||
}
|
||||
} elseif (!multibranch_active()) {
|
||||
if ($user->active_branch_id) {
|
||||
$user->update([
|
||||
'active_branch_id' => NULL
|
||||
]);
|
||||
} elseif ($user->branch_id && !$branch->is_main) {
|
||||
|
||||
Auth::logout();
|
||||
return response()->json([
|
||||
'message' => 'Multibranch is not allowed in your current package, please upgrade your subscription plan.',
|
||||
], 406);
|
||||
}
|
||||
} elseif (!$branch && $user->branch_id) {
|
||||
Auth::logout();
|
||||
return response()->json([
|
||||
'message' => 'Your current branch has been deleted, Please contact with manager.',
|
||||
], 406);
|
||||
} elseif ($business && !$business->status) {
|
||||
Auth::logout();
|
||||
return response()->json([
|
||||
'message' => 'Your business is inactive. Please contact your administrator.',
|
||||
], 406);
|
||||
}
|
||||
|
||||
if ($user->role != 'staff' && $user->role != 'shop-owner') {
|
||||
return response()->json([
|
||||
'message' => 'You can not login as ' . $user->role . ' from the app!'
|
||||
], 406);
|
||||
}
|
||||
|
||||
if ($user->remember_token && !$user->business_id) { // If user didn't verify email
|
||||
|
||||
$code = random_int(100000, 999999);
|
||||
$expire = now()->addMinutes(env('OTP_VISIBILITY_TIME') ?? 3);
|
||||
$data = [
|
||||
'code' => $code,
|
||||
'name' => $request->name,
|
||||
];
|
||||
|
||||
if (env('MAIL_USERNAME')) {
|
||||
if (env('QUEUE_MAIL')) {
|
||||
Mail::to($request->email)->queue(new WelcomeMail($data));
|
||||
} else {
|
||||
Mail::to($request->email)->send(new WelcomeMail($data));
|
||||
}
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => __('Mail service is not configured. Please contact your administrator.'),
|
||||
], 406);
|
||||
}
|
||||
|
||||
User::where('email', $request->email)->first()->update(['remember_token' => $code, 'email_verified_at' => $expire]);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'An otp code has been sent to your email. Please check and confirm.',
|
||||
], 201);
|
||||
}
|
||||
|
||||
$currency = UserCurrency::select('id', 'name', 'code', 'symbol', 'position')->where('business_id', $user->business_id)->first();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'User login successfully!',
|
||||
'data' => [
|
||||
'is_setup' => $user->business_id ? true : false,
|
||||
'token' => $user->createToken('createToken')->plainTextToken,
|
||||
'currency' => $currency ?? Currency::select('id', 'name', 'code', 'symbol', 'position')->where('is_default', 1)->first(),
|
||||
],
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => 'Invalid email or password!'
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
|
||||
protected function setAccessTokenExpiration(NewAccessToken $accessToken)
|
||||
{
|
||||
$expiration = now()->addMinutes(Config::get('sanctum.expiration'));
|
||||
|
||||
DB::table('personal_access_tokens')
|
||||
->where('id', $accessToken->accessToken->id)
|
||||
->update(['expires_at' => $expiration]);
|
||||
}
|
||||
|
||||
public function signOut(): JsonResponse
|
||||
{
|
||||
$currentToken = auth()->user()->currentAccessToken();
|
||||
|
||||
if ($currentToken) {
|
||||
$currentToken->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Sign out successfully'),
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => __('Unauthorized'),
|
||||
], 401);
|
||||
}
|
||||
}
|
||||
|
||||
public function refreshToken()
|
||||
{
|
||||
if (auth()->user()->tokens()) {
|
||||
|
||||
auth()->user()->currentAccessToken()->delete();
|
||||
$data['token'] = auth()->user()->createToken('createToken')->plainTextToken;
|
||||
return response()->json($data);
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => __('Unauthorized'),
|
||||
], 401);
|
||||
}
|
||||
}
|
||||
|
||||
public function resendOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'email' => 'required|email|exists:users,email',
|
||||
]);
|
||||
|
||||
$code = random_int(100000, 999999);
|
||||
$otpController = new RegisteredUserController();
|
||||
$visibility_time = $otpController->getOtpTimeInSeconds();
|
||||
$expire = now()->addSeconds($visibility_time);
|
||||
|
||||
$data = [
|
||||
'code' => $code,
|
||||
'name' => $request->name,
|
||||
];
|
||||
|
||||
if (env('MAIL_USERNAME')) {
|
||||
if (env('QUEUE_MAIL')) {
|
||||
Mail::to($request->email)->queue(new WelcomeMail($data));
|
||||
} else {
|
||||
Mail::to($request->email)->send(new WelcomeMail($data));
|
||||
}
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => __('Mail service is not configured. Please contact your administrator.'),
|
||||
], 406);
|
||||
}
|
||||
|
||||
User::where('email', $request->email)->first()->update(['remember_token' => $code, 'email_verified_at' => $expire]);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'An otp code has been sent to your email. Please check and confirm.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function moduleCheck()
|
||||
{
|
||||
if (moduleCheck(request('module_name'))) {
|
||||
return response()->json([
|
||||
'status' => true
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => false
|
||||
]);
|
||||
}
|
||||
}
|
||||
168
app/Http/Controllers/Api/BankController.php
Normal file
168
app/Http/Controllers/Api/BankController.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PaymentType;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BankController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = PaymentType::with('branch:id,name')->where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'branch_id' => 'nullable|exists:branches,id',
|
||||
'opening_balance' => 'nullable|numeric|min:0',
|
||||
'opening_date' => 'nullable|date',
|
||||
'show_in_invoice' => 'boolean',
|
||||
'meta' => 'nullable|array',
|
||||
'meta.account_number' => 'nullable|string|max:255',
|
||||
'meta.routing_number' => 'nullable|string|max:255',
|
||||
'meta.upi_id' => 'nullable|string|max:255',
|
||||
'meta.bank_name' => 'nullable|string|max:255',
|
||||
'meta.account_holder' => 'nullable|string|max:255',
|
||||
'meta.branch' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$forbidden = ['cash', 'cheque'];
|
||||
if (in_array(strtolower($request->name), $forbidden)) {
|
||||
return response()->json([
|
||||
'message' => __('You cannot create a bank account with this name.'),
|
||||
], 422);
|
||||
}
|
||||
|
||||
$data = PaymentType::create($request->except('business_id', 'show_in_invoice', 'balance', 'opening_balance', 'meta') + [
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'opening_balance' => $request->opening_balance ?? 0,
|
||||
'balance' => $request->opening_balance ?? 0,
|
||||
'show_in_invoice' => $request->show_in_invoice,
|
||||
'meta' => [
|
||||
'account_number' => $request->account_number,
|
||||
'routing_number' => $request->routing_number,
|
||||
'upi_id' => $request->upi_id,
|
||||
'bank_name' => $request->bank_name,
|
||||
'account_holder' => $request->account_holder,
|
||||
'branch' => $request->branch,
|
||||
]
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$data = PaymentType::with('branch:id,name')->where('business_id', auth()->user()->business_id)->find($id);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'branch_id' => 'nullable|exists:branches,id',
|
||||
'opening_balance' => 'nullable|numeric|min:0',
|
||||
'opening_date' => 'nullable|date',
|
||||
'show_in_invoice' => 'boolean',
|
||||
'meta' => 'nullable|array',
|
||||
'meta.account_number' => 'nullable|string|max:255',
|
||||
'meta.routing_number' => 'nullable|string|max:255',
|
||||
'meta.upi_id' => 'nullable|string|max:255',
|
||||
'meta.bank_name' => 'nullable|string|max:255',
|
||||
'meta.account_holder' => 'nullable|string|max:255',
|
||||
'meta.branch' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$payment_type = PaymentType::findOrFail($id);
|
||||
|
||||
$forbidden = ['cash', 'cheque'];
|
||||
if (in_array(strtolower($request->name), $forbidden)) {
|
||||
return response()->json([
|
||||
'message' => __('You cannot create a bank account with this name.'),
|
||||
], 422);
|
||||
}
|
||||
|
||||
$hasTransactions = $payment_type->transactions()->exists();
|
||||
|
||||
if ($hasTransactions) {
|
||||
return response()->json([
|
||||
'message' => __("You can't Change opening balance because this bank already has transactions.")
|
||||
], 422);
|
||||
}
|
||||
|
||||
$updateData = $request->except('business_id', 'show_in_invoice', 'balance', 'opening_balance', 'meta');
|
||||
|
||||
// Add controlled fields
|
||||
$updateData['business_id'] = auth()->user()->business_id;
|
||||
$updateData['show_in_invoice'] = $request->show_in_invoice;
|
||||
|
||||
// Only update balances if no transactions exist
|
||||
if (!$hasTransactions) {
|
||||
$updateData['opening_balance'] = $request->opening_balance ?? 0;
|
||||
$updateData['balance'] = $request->opening_balance ?? 0;
|
||||
}
|
||||
|
||||
// Build meta explicitly
|
||||
$updateData['meta'] = [
|
||||
'account_number' => $request->account_number,
|
||||
'routing_number' => $request->routing_number,
|
||||
'upi_id' => $request->upi_id,
|
||||
'bank_name' => $request->bank_name,
|
||||
'account_holder' => $request->account_holder,
|
||||
'branch' => $request->branch,
|
||||
];
|
||||
|
||||
$payment_type->update($updateData);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $payment_type,
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$paymentType = PaymentType::find($id);
|
||||
|
||||
if (!$paymentType) {
|
||||
return response()->json([
|
||||
'message' => __('Data not found.'),
|
||||
'data' => null,
|
||||
], 404);
|
||||
}
|
||||
|
||||
// Check if this payment type is used in any transaction
|
||||
$hasTransactions = $paymentType->transactions()->exists();
|
||||
|
||||
$balance = $paymentType->balance ?? 0;
|
||||
|
||||
if ($hasTransactions && $balance != 0) {
|
||||
return response()->json([
|
||||
'message' => __('Bank can’t be deleted. It has transactions or balance.')
|
||||
], 400);
|
||||
}
|
||||
|
||||
$paymentType->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
357
app/Http/Controllers/Api/BankTransactionController.php
Normal file
357
app/Http/Controllers/Api/BankTransactionController.php
Normal file
@@ -0,0 +1,357 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\HasUploader;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class BankTransactionController extends Controller
|
||||
{
|
||||
use HasUploader;
|
||||
|
||||
public function index()
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
$payment_type_id = request('bank_id');
|
||||
|
||||
$data = Transaction::with('user:id,name', 'branch:id,name')
|
||||
->where('business_id', $business_id)
|
||||
->where(function ($query) use ($payment_type_id) {
|
||||
$query->where('payment_type_id', $payment_type_id)
|
||||
->orWhere('from_bank', $payment_type_id)
|
||||
->orWhere('to_bank', $payment_type_id);
|
||||
})
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$transaction = Transaction::with('user:id,name', 'branch:id,name')
|
||||
->where('business_id', $business_id)
|
||||
->where('id', $id)
|
||||
->first();
|
||||
|
||||
if (!$transaction) {
|
||||
return response()->json([
|
||||
'message' => __('Transaction not found.'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Transaction fetched successfully.'),
|
||||
'data' => $transaction,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'from' => 'required|exists:payment_types,id',
|
||||
'type' => 'nullable|in:credit,debit',
|
||||
'transaction_type' => 'required|in:bank_to_bank,bank_to_cash,adjust_bank',
|
||||
'amount' => 'required|numeric|min:0.01',
|
||||
'date' => 'nullable|date',
|
||||
'image' => 'nullable|image|mimes:jpg,png,jpeg,svg',
|
||||
'note' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$business_id = auth()->user()->business_id;
|
||||
$amount = $request->amount ?? 0;
|
||||
$type = 'transfer';
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$fromBank = PaymentType::find($request->from);
|
||||
|
||||
// Prevent transferring to the same bank
|
||||
if ($request->transaction_type == 'bank_to_bank' && $request->from == $request->to) {
|
||||
return response()->json([
|
||||
'message' => 'Cannot transfer between the same bank account.'
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Update balances based on transaction type
|
||||
if ($request->transaction_type == 'bank_to_bank') {
|
||||
$toBank = PaymentType::find($request->to);
|
||||
|
||||
if ($fromBank->balance < $amount) {
|
||||
return response()->json([
|
||||
'message' => 'Insufficient balance in source bank account.'
|
||||
], 400);
|
||||
}
|
||||
|
||||
$fromBank->decrement('balance', $amount);
|
||||
$toBank->increment('balance', $amount);
|
||||
|
||||
} elseif ($request->transaction_type == 'bank_to_cash') {
|
||||
if ($fromBank->balance < $amount) {
|
||||
return response()->json([
|
||||
'message' => 'Insufficient balance in selected bank.'
|
||||
], 400);
|
||||
}
|
||||
|
||||
$fromBank->decrement('balance', $amount);
|
||||
|
||||
} elseif ($request->transaction_type == 'adjust_bank') {
|
||||
$type = $request->type;
|
||||
if ($type == 'credit' && $fromBank->balance < $amount) {
|
||||
return response()->json([
|
||||
'message' => 'Cannot decrease below zero balance.'
|
||||
], 400);
|
||||
}
|
||||
if( $type == 'credit'){
|
||||
$fromBank->increment('balance', $amount);
|
||||
}else{
|
||||
$fromBank->decrement('balance', $amount);
|
||||
}
|
||||
}
|
||||
|
||||
$data = Transaction::create([
|
||||
'business_id' => $business_id,
|
||||
'user_id' => auth()->id(),
|
||||
'type' => $type,
|
||||
'platform' => 'bank',
|
||||
'transaction_type' => $request->transaction_type,
|
||||
'amount' => $amount,
|
||||
'from_bank' => $request->from,
|
||||
'to_bank' => ($request->transaction_type == 'bank_to_bank') ? $request->to : null,
|
||||
'date' => $request->date ?? now(),
|
||||
'image' => $request->image ? $this->upload($request, 'image') : NULL,
|
||||
'note' => $request->note,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json([
|
||||
'message' => $e->getMessage()
|
||||
], 406);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'from' => 'required|exists:payment_types,id',
|
||||
'type' => 'nullable|in:credit,debit',
|
||||
'transaction_type' => 'required|in:bank_to_bank,bank_to_cash,adjust_bank',
|
||||
'amount' => 'required|numeric|min:0.01',
|
||||
'date' => 'nullable|date',
|
||||
'image' => 'nullable|image|mimes:jpg,png,jpeg,svg',
|
||||
'note' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$transaction = Transaction::findOrFail($id);
|
||||
$fromBank = PaymentType::find($request->from);
|
||||
$newAmount = $request->amount;
|
||||
$newType = $request->transaction_type;
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Transaction type Same
|
||||
if ($transaction->transaction_type === $request->transaction_type) {
|
||||
|
||||
if ($newType === 'bank_to_bank') {
|
||||
$toBank = PaymentType::find($request->to);
|
||||
|
||||
// Adjust balance difference
|
||||
$diff = $newAmount - $transaction->amount;
|
||||
if ($fromBank->balance < $diff && $diff > 0) {
|
||||
return response()->json(['message' => 'Insufficient balance.'], 400);
|
||||
}
|
||||
|
||||
$fromBank->decrement('balance', $diff);
|
||||
$toBank->increment('balance', $diff);
|
||||
|
||||
} elseif ($newType === 'bank_to_cash') {
|
||||
$diff = $newAmount - $transaction->amount;
|
||||
if ($fromBank->balance < $diff && $diff > 0) {
|
||||
return response()->json(['message' => 'Insufficient balance.'], 400);
|
||||
}
|
||||
$fromBank->decrement('balance', $diff);
|
||||
|
||||
} elseif ($newType === 'adjust_bank') {
|
||||
$bankType = $request->type;
|
||||
$oldType = $transaction->type;
|
||||
$oldAmount = $transaction->amount;
|
||||
|
||||
if ($bankType === $oldType) {
|
||||
// Same type: adjust by difference
|
||||
$diff = $newAmount - $oldAmount;
|
||||
if ($bankType == 'credit') {
|
||||
$fromBank->increment('balance', $diff);
|
||||
} else {
|
||||
$fromBank->decrement('balance', $diff);
|
||||
}
|
||||
} else {
|
||||
// Different type: reverse old and apply new
|
||||
if ($oldType == 'credit') $fromBank->decrement('balance', $oldAmount);
|
||||
else $fromBank->increment('balance', $oldAmount);
|
||||
|
||||
if ($bankType == 'credit') $fromBank->increment('balance', $newAmount);
|
||||
else $fromBank->decrement('balance', $newAmount);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Transaction type changed
|
||||
else {
|
||||
// Reverse old transaction completely
|
||||
if ($transaction->transaction_type === 'bank_to_bank') {
|
||||
$oldFrom = PaymentType::find($transaction->from_bank);
|
||||
$oldTo = PaymentType::find($transaction->to_bank);
|
||||
$oldFrom->increment('balance', $transaction->amount);
|
||||
$oldTo->decrement('balance', $transaction->amount);
|
||||
} elseif ($transaction->transaction_type === 'bank_to_cash') {
|
||||
$oldFrom = PaymentType::find($transaction->from_bank);
|
||||
$oldFrom->increment('balance', $transaction->amount);
|
||||
} elseif ($transaction->transaction_type === 'adjust_bank') {
|
||||
$oldFrom = PaymentType::find($transaction->from_bank);
|
||||
$transaction->type === 'credit'
|
||||
? $oldFrom->decrement('balance', $transaction->amount)
|
||||
: $oldFrom->increment('balance', $transaction->amount);
|
||||
}
|
||||
|
||||
// Apply new transaction
|
||||
if ($newType === 'bank_to_bank') {
|
||||
$toBank = PaymentType::find($request->to);
|
||||
if ($fromBank->balance < $newAmount) {
|
||||
return response()->json(['message' => 'Insufficient balance.'], 400);
|
||||
}
|
||||
$fromBank->decrement('balance', $newAmount);
|
||||
$toBank->increment('balance', $newAmount);
|
||||
} elseif ($newType === 'bank_to_cash') {
|
||||
if ($fromBank->balance < $newAmount) {
|
||||
return response()->json(['message' => 'Insufficient balance.'], 400);
|
||||
}
|
||||
$fromBank->decrement('balance', $newAmount);
|
||||
} elseif ($newType === 'adjust_bank') {
|
||||
if ($request->type == 'credit') $fromBank->increment('balance', $newAmount);
|
||||
else $fromBank->decrement('balance', $newAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// Update transaction record
|
||||
$transaction->update([
|
||||
'type' => $newType === 'adjust_bank' ? $request->type : 'transfer',
|
||||
'transaction_type' => $newType,
|
||||
'amount' => $newAmount,
|
||||
'from_bank' => $request->from,
|
||||
'to_bank' => $newType === 'bank_to_bank' ? $request->to : null,
|
||||
'date' => $request->date ?? now(),
|
||||
'image' => $request->image ? $this->upload($request, 'image', $transaction->image) : $transaction->image,
|
||||
'note' => $request->note,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $transaction,
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['message' => 'Error: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
$transaction = Transaction::findOrFail($id);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$fromBank = PaymentType::find($transaction->from_bank);
|
||||
$toBank = PaymentType::find($transaction->to_bank);
|
||||
$amount = $transaction->amount;
|
||||
|
||||
// Allow only bank platform transactions to be deleted
|
||||
if ($transaction->platform !== 'bank') {
|
||||
return response()->json([
|
||||
'message' => 'Cannot delete here, please delete from ' . ucfirst($transaction->platform) . ' section.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Reverse balance changes based on transaction type
|
||||
switch ($transaction->transaction_type) {
|
||||
case 'bank_to_bank':
|
||||
if ($toBank && $fromBank) {
|
||||
// Ensure receiver bank has enough balance before reversing
|
||||
if ($toBank->balance < $amount) {
|
||||
return response()->json([
|
||||
'message' => 'Insufficient balance in ' . $toBank->name . ' to reverse this transaction.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
$fromBank->increment('balance', $amount);
|
||||
$toBank->decrement('balance', $amount);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bank_to_cash':
|
||||
if ($fromBank) {
|
||||
$fromBank->increment('balance', $amount);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'adjust_bank':
|
||||
if ($fromBank) {
|
||||
if ($transaction->type === 'credit') {
|
||||
// Previously increased, so now decrease
|
||||
if ($fromBank->balance < $amount) {
|
||||
return response()->json([
|
||||
'message' => 'Insufficient balance in ' . $fromBank->name . ' to reverse this transaction.',
|
||||
], 400);
|
||||
}
|
||||
$fromBank->decrement('balance', $amount);
|
||||
} else {
|
||||
// Previously decreased, so now increase
|
||||
$fromBank->increment('balance', $amount);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (file_exists($transaction->image)) {
|
||||
Storage::delete($transaction->image);
|
||||
}
|
||||
|
||||
$transaction->delete();
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['message' => 'Error: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
26
app/Http/Controllers/Api/BulkUploadControler.php
Normal file
26
app/Http/Controllers/Api/BulkUploadControler.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Imports\ProductImport;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
|
||||
class BulkUploadControler extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'file' => 'required|file|mimes:xlsx,xls,csv'
|
||||
]);
|
||||
|
||||
$businessId = auth()->user()->business_id;
|
||||
|
||||
Excel::import(new ProductImport($businessId), $request->file('file'));
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Bulk upload successfully.')
|
||||
]);
|
||||
}
|
||||
}
|
||||
19
app/Http/Controllers/Api/BusinessCategoryController.php
Normal file
19
app/Http/Controllers/Api/BusinessCategoryController.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\BusinessCategory;
|
||||
|
||||
class BusinessCategoryController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = BusinessCategory::whereStatus(1)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'data' => $data,
|
||||
'message' => __('Data fetched successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
386
app/Http/Controllers/Api/BusinessController.php
Normal file
386
app/Http/Controllers/Api/BusinessController.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Option;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Models\Business;
|
||||
use App\Models\Currency;
|
||||
use App\Helpers\HasUploader;
|
||||
use App\Models\UserCurrency;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\PlanSubscribe;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PaymentType;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class BusinessController extends Controller
|
||||
{
|
||||
use HasUploader;
|
||||
|
||||
public function index()
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
// Ensure currency exists
|
||||
$business_currency = UserCurrency::select('id', 'name', 'code', 'symbol', 'position')
|
||||
->where('business_id', $business_id)
|
||||
->first();
|
||||
|
||||
if (!$business_currency) {
|
||||
$currency = Currency::where('is_default', 1)->first();
|
||||
UserCurrency::create([
|
||||
'name' => $currency->name,
|
||||
'code' => $currency->code,
|
||||
'rate' => $currency->rate,
|
||||
'business_id' => $business_id,
|
||||
'symbol' => $currency->symbol,
|
||||
'currency_id' => $currency->id,
|
||||
'position' => $currency->position,
|
||||
'country_name' => $currency->country_name,
|
||||
]);
|
||||
}
|
||||
|
||||
// Fetch user and business info
|
||||
$user = User::select('id', 'name', 'role', 'visibility', 'lang', 'email', 'branch_id', 'active_branch_id')->findOrFail(auth()->id());
|
||||
$business = Business::with('category:id,name', 'enrolled_plan:id,plan_id,business_id,price,duration,allow_multibranch', 'enrolled_plan.plan:id,subscriptionName')->findOrFail($business_id);
|
||||
|
||||
//admin setting option
|
||||
$generalValue = Option::where('key', 'general')->first()->value ?? [];
|
||||
$develop_by_level = $generalValue['admin_footer_text'] ?? '';
|
||||
$develop_by = $generalValue['admin_footer_link_text'] ?? '';
|
||||
$develop_by_link = $generalValue['admin_footer_link'] ?? '';
|
||||
|
||||
// Get business settings option
|
||||
$option = Option::where('key', 'business-settings')
|
||||
->where('value', 'LIKE', '%"business_id":%' . $business_id . '%')
|
||||
->get()
|
||||
->firstWhere('value.business_id', $business_id);
|
||||
|
||||
$invoice_logo = $option->value['invoice_logo'] ?? null;
|
||||
$a4_invoice_logo = $option->value['a4_invoice_logo'] ?? null;
|
||||
$thermal_invoice_logo = $option->value['thermal_invoice_logo'] ?? null;
|
||||
$invoice_scanner_logo = $option->value['invoice_scanner_logo'] ?? null;
|
||||
$sale_rounding_option = $option->value['sale_rounding_option'] ?? 'none';
|
||||
$note_label = $option->value['note_label'] ?? null;
|
||||
$note = $option->value['note'] ?? null;
|
||||
$gratitude_message = $option->value['gratitude_message'] ?? null;
|
||||
$warranty_void_label = $option->value['warranty_void_label'] ?? null;
|
||||
$warranty_void = $option->value['warranty_void'] ?? null;
|
||||
|
||||
$data = array_merge(
|
||||
$business->toArray(),
|
||||
['user' => $user->toArray() + ['active_branch' => $user->active_branch]],
|
||||
['business_currency' => $business_currency],
|
||||
['invoice_logo' => $invoice_logo],
|
||||
['a4_invoice_logo' => $a4_invoice_logo],
|
||||
['thermal_invoice_logo' => $thermal_invoice_logo],
|
||||
['invoice_scanner_logo' => $invoice_scanner_logo],
|
||||
['sale_rounding_option' => $sale_rounding_option],
|
||||
['invoice_size' => !empty(invoice_setting()) && moduleCheck('ThermalPrinterAddon') ? invoice_setting() : null],
|
||||
['invoice_language' => !empty(invoice_language()) ? invoice_language() : ''],
|
||||
['note' => $note],
|
||||
['note_label' => $note_label],
|
||||
['gratitude_message' => $gratitude_message],
|
||||
['warranty_void_label' => $warranty_void_label],
|
||||
['warranty_void' => $warranty_void],
|
||||
['show_note' => (int) ($option->value['show_note'] ?? 0)],
|
||||
['show_gratitude_msg' => (int) ($option->value['show_gratitude_msg'] ?? 0)],
|
||||
['show_invoice_scanner_logo' => (int) ($option->value['show_invoice_scanner_logo'] ?? 0)],
|
||||
['show_a4_invoice_logo' => (int) ($option->value['show_a4_invoice_logo'] ?? 0)],
|
||||
['show_thermal_invoice_logo' => (int) ($option->value['show_thermal_invoice_logo'] ?? 0)],
|
||||
['show_warranty' => (int) ($option->value['show_warranty'] ?? 0)],
|
||||
['develop_by_level' => $develop_by_level],
|
||||
['develop_by' => $develop_by],
|
||||
['develop_by_link' => $develop_by_link],
|
||||
['branch_count' => branch_count()],
|
||||
[
|
||||
'addons' => [
|
||||
'AffiliateAddon' => moduleCheck('AffiliateAddon'),
|
||||
'MultiBranchAddon' => moduleCheck('MultiBranchAddon'),
|
||||
'WarehouseAddon' => moduleCheck('WarehouseAddon'),
|
||||
'ThermalPrinterAddon' => moduleCheck('ThermalPrinterAddon'),
|
||||
'HrmAddon' => moduleCheck('HrmAddon'),
|
||||
'CustomDomainAddon' => moduleCheck('CustomDomainAddon'),
|
||||
'SerialCodeAddon' => moduleCheck('SerialCodeAddon')
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'address' => 'nullable|max:250',
|
||||
'companyName' => 'required|max:250',
|
||||
'pictureUrl' => 'nullable|image|max:5120',
|
||||
'shopOpeningBalance' => 'nullable|numeric',
|
||||
'phoneNumber' => 'nullable',
|
||||
'business_category_id' => 'required|exists:business_categories,id',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
||||
$user = auth()->user();
|
||||
$free_plan = Plan::where('subscriptionPrice', '<=', 0)->orWhere('offerPrice', '<=', 0)->first();
|
||||
|
||||
$business = Business::create($request->except('pictureUrl') + [
|
||||
'phoneNumber' => $request->phoneNumber,
|
||||
'subscriptionDate' => $free_plan ? now() : NULL,
|
||||
'will_expire' => $free_plan ? now()->addDays($free_plan->duration) : NULL,
|
||||
'pictureUrl' => $request->pictureUrl ? $this->upload($request, 'pictureUrl') : NULL
|
||||
]);
|
||||
|
||||
PaymentType::create([
|
||||
'name' => "Cash",
|
||||
'business_id' => $business->id
|
||||
]);
|
||||
|
||||
$user->update([
|
||||
'business_id' => $business->id,
|
||||
'phone' => $request->phoneNumber,
|
||||
'name' => $business->companyName,
|
||||
]);
|
||||
|
||||
$currency = Currency::where('is_default', 1)->first();
|
||||
UserCurrency::create([
|
||||
'business_id' => $business->id,
|
||||
'currency_id' => $currency->id,
|
||||
'name' => $currency->name,
|
||||
'country_name' => $currency->country_name,
|
||||
'code' => $currency->code,
|
||||
'rate' => $currency->rate,
|
||||
'symbol' => $currency->symbol,
|
||||
'position' => $currency->position,
|
||||
]);
|
||||
|
||||
if ($free_plan) {
|
||||
$subscribe = PlanSubscribe::create([
|
||||
'plan_id' => $free_plan->id,
|
||||
'business_id' => $business->id,
|
||||
'duration' => $free_plan->duration,
|
||||
'allow_multibranch' => $free_plan->allow_multibranch,
|
||||
]);
|
||||
|
||||
$business->update([
|
||||
'plan_subscribe_id' => $subscribe->id,
|
||||
]);
|
||||
}
|
||||
|
||||
Cache::forget('plan-data-' . $business->id);
|
||||
|
||||
DB::commit();
|
||||
return response()->json([
|
||||
'message' => __('Business setup completed.'),
|
||||
]);
|
||||
} catch (\Throwable $th) {
|
||||
DB::rollback();
|
||||
return response()->json(__('Something went wrong, Please contact with admin.'), 403);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Request $request, Business $business)
|
||||
{
|
||||
$request->validate([
|
||||
'address' => 'nullable|max:250',
|
||||
'companyName' => 'nullable|required_if:sale_rounding_option,!=,null|max:250',
|
||||
'business_category_id' => 'nullable|required_if:sale_rounding_option,!=,null|exists:business_categories,id',
|
||||
'pictureUrl' => 'nullable|image|max:5120',
|
||||
'show_company_name' => 'nullable|boolean',
|
||||
'show_phone_number' => 'nullable|boolean',
|
||||
'show_address' => 'nullable|boolean',
|
||||
'show_email' => 'nullable|boolean',
|
||||
'show_vat' => 'nullable|boolean',
|
||||
'invoice_logo' => 'nullable|image|max:5120',
|
||||
'a4_invoice_logo' => 'nullable|image|max:5120',
|
||||
'thermal_invoice_logo' => 'nullable|image|max:5120',
|
||||
'invoice_scanner_logo' => 'nullable|image|max:5120',
|
||||
'sale_rounding_option' => 'nullable|in:none,round_up,nearest_whole_number,nearest_0.05,nearest_0.1,nearest_0.5',
|
||||
'phoneNumber' => 'nullable',
|
||||
'invoice_size' => 'nullable|string|max:100',
|
||||
'invoice_language' => 'nullable|string|max:100',
|
||||
'gratitude_message' => 'nullable|string|max:100',
|
||||
'warranty_void_label' => 'nullable|string',
|
||||
'warranty_void' => 'nullable|string',
|
||||
'show_note' => 'nullable|boolean',
|
||||
'show_gratitude_msg' => 'nullable|boolean',
|
||||
'show_invoice_scanner_logo' => 'nullable|boolean',
|
||||
'show_a4_invoice_logo' => 'nullable|boolean',
|
||||
'show_thermal_invoice_logo' => 'nullable|boolean',
|
||||
'show_warranty' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
$business->update([
|
||||
'meta' => [
|
||||
'show_company_name' => (int) ($request->show_company_name ?? 0),
|
||||
'show_phone_number' => (int) ($request->show_phone_number ?? 0),
|
||||
'show_address' => (int) ($request->show_address ?? 0),
|
||||
'show_email' => (int) ($request->show_email ?? 0),
|
||||
'show_vat' => (int) ($request->show_vat ?? 0),
|
||||
]
|
||||
]);
|
||||
|
||||
// Update when sale_rounding_option is not provided
|
||||
if (!$request->sale_rounding_option) {
|
||||
auth()->user()->update([
|
||||
'name' => $request->companyName,
|
||||
'phone' => $request->phoneNumber,
|
||||
]);
|
||||
|
||||
$business->update($request->except('pictureUrl', 'meta') + [
|
||||
'pictureUrl' => $request->pictureUrl ? $this->upload($request, 'pictureUrl', $business->pictureUrl) : $business->pictureUrl,
|
||||
]);
|
||||
}
|
||||
|
||||
// Update or insert business settings
|
||||
$setting = Option::where('key', 'business-settings')
|
||||
->where('value', 'LIKE', '%"business_id":%' . $business->id . '%')
|
||||
->get()
|
||||
->firstWhere('value.business_id', $business->id);
|
||||
|
||||
$invoiceLogo = $request->invoice_logo ? $this->upload($request, 'invoice_logo', $setting->value['invoice_logo'] ?? null) : ($setting->value['invoice_logo'] ?? null);
|
||||
$a4_invoice_logo = $request->a4_invoice_logo ? $this->upload($request, 'a4_invoice_logo', $setting->value['a4_invoice_logo'] ?? null) : ($setting->value['a4_invoice_logo'] ?? null);
|
||||
$thermal_invoice_logo = $request->thermal_invoice_logo ? $this->upload($request, 'thermal_invoice_logo', $setting->value['thermal_invoice_logo'] ?? null) : ($setting->value['thermal_invoice_logo'] ?? null);
|
||||
$invoice_scanner_logo = $request->invoice_scanner_logo ? $this->upload($request, 'invoice_scanner_logo', $setting->value['invoice_scanner_logo'] ?? null) : ($setting->value['invoice_scanner_logo'] ?? null);
|
||||
|
||||
$settingData = [
|
||||
'business_id' => $business->id,
|
||||
'invoice_logo' => $invoiceLogo,
|
||||
'a4_invoice_logo' => $a4_invoice_logo,
|
||||
'thermal_invoice_logo' => $thermal_invoice_logo,
|
||||
'invoice_scanner_logo' => $invoice_scanner_logo,
|
||||
'sale_rounding_option' => $request->sale_rounding_option ?? 'none',
|
||||
'note_label' => $request->note_label,
|
||||
'note' => $request->note,
|
||||
'gratitude_message' => $request->gratitude_message,
|
||||
'warranty_void_label' => $request->warranty_void_label,
|
||||
'warranty_void' => $request->warranty_void,
|
||||
'vat_name' => $request->vat_name,
|
||||
'vat_no' => $request->vat_no,
|
||||
'show_note' => $request->show_note ?? 0,
|
||||
'show_gratitude_msg' => $request->show_gratitude_msg ?? 0,
|
||||
'show_invoice_scanner_logo' => $request->show_invoice_scanner_logo ?? 0,
|
||||
'show_a4_invoice_logo' => $request->show_a4_invoice_logo ?? 0,
|
||||
'show_thermal_invoice_logo' => $request->show_thermal_invoice_logo ?? 0,
|
||||
'show_warranty' => $request->show_warranty ?? 0,
|
||||
];
|
||||
|
||||
if ($setting) {
|
||||
$setting->update([
|
||||
'value' => array_merge($setting->value, $settingData),
|
||||
]);
|
||||
} else {
|
||||
Option::create([
|
||||
'key' => 'business-settings',
|
||||
'value' => $settingData,
|
||||
]);
|
||||
}
|
||||
|
||||
// Update Invoice Settings
|
||||
if ($request->filled('invoice_size')) {
|
||||
$invoiceKey = 'invoice_setting_' . $business->id;
|
||||
|
||||
Option::updateOrCreate(
|
||||
['key' => $invoiceKey],
|
||||
['value' => $request->invoice_size]
|
||||
);
|
||||
|
||||
Cache::forget($invoiceKey);
|
||||
}
|
||||
|
||||
if ($request->filled('invoice_language')) {
|
||||
$invoice_language_key = 'invoice_language_' . $business->id;
|
||||
|
||||
Option::updateOrCreate(
|
||||
['key' => $invoice_language_key],
|
||||
['value' => $request->invoice_language]
|
||||
);
|
||||
|
||||
Cache::forget($invoice_language_key);
|
||||
}
|
||||
|
||||
Cache::forget("business_setting_{$business->id}");
|
||||
Cache::forget("business_sale_rounding_{$business->id}");
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'business' => $business,
|
||||
'invoice_logo' => $invoiceLogo,
|
||||
'a4_invoice_logo' => $a4_invoice_logo,
|
||||
'thermal_invoice_logo' => $thermal_invoice_logo,
|
||||
'invoice_scanner_logo' => $invoice_scanner_logo,
|
||||
'sale_rounding_option' => $request->sale_rounding_option,
|
||||
'invoice_size' => $request->invoice_size,
|
||||
'invoice_language' => $request->invoice_language,
|
||||
'note_label' => $request->note_label,
|
||||
'note' => $request->note,
|
||||
'gratitude_message' => $request->gratitude_message,
|
||||
'warranty_void_label' => $request->warranty_void_label,
|
||||
'warranty_void' => $request->warranty_void,
|
||||
'vat_name' => $request->vat_name,
|
||||
'vat_no' => $request->vat_no,
|
||||
'show_note' => (int) $request->show_note ?? 0,
|
||||
'show_gratitude_msg' => (int) $request->show_gratitude_msg ?? 0,
|
||||
'show_invoice_scanner_logo' => (int) $request->show_invoice_scanner_logo ?? 0,
|
||||
'show_a4_invoice_logo' => (int) $request->show_a4_invoice_logo ?? 0,
|
||||
'show_thermal_invoice_logo' => (int) $request->show_thermal_invoice_logo ?? 0,
|
||||
'show_warranty' => (int) $request->show_warranty ?? 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function updateExpireDate(Request $request)
|
||||
{
|
||||
$days = $request->query('days', 0);
|
||||
$operation = $request->query('operation');
|
||||
$business = Business::where('id', auth()->user()->business_id)->first();
|
||||
if (!$business) {
|
||||
return response()->json([
|
||||
'message' => 'Business not found.',
|
||||
], 404);
|
||||
}
|
||||
if ($operation === 'add') {
|
||||
$business->will_expire = now()->addDays($days);
|
||||
} elseif ($operation === 'sub') {
|
||||
$business->will_expire = now()->subDays($days);
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => 'Invalid operation. Use "add" or "sub".',
|
||||
], 400);
|
||||
}
|
||||
$business->save();
|
||||
return response()->json([
|
||||
'message' => 'Expiry date updated successfully.',
|
||||
'will_expire' => $business->will_expire,
|
||||
]);
|
||||
}
|
||||
|
||||
public function deleteBusiness(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'password' => 'required|string',
|
||||
]);
|
||||
|
||||
$user = auth()->user();
|
||||
|
||||
if (!Hash::check($request->password, $user->password)) {
|
||||
return response()->json([
|
||||
'message' => __('The provided password is incorrect.'),
|
||||
], 406);
|
||||
}
|
||||
|
||||
Business::where('id', $user->business_id)->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Option;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
|
||||
class BusinessCurrencySettingController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
|
||||
$currency_setting_key = 'currency_setting_' . $businessId;
|
||||
$currency_setting = Option::where('key', $currency_setting_key)->first();
|
||||
|
||||
if ($currency_setting) {
|
||||
return response()->json([
|
||||
'message' => __('Currency data fetched successfully.'),
|
||||
'currency_data' => $currency_setting->value,
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => __('Currency not found.'),
|
||||
'currency_data' => null,
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'currency' => 'required|string|max:100|in:us,european',
|
||||
]);
|
||||
|
||||
$key = 'currency_setting_' . auth()->user()->business_id;
|
||||
|
||||
Option::updateOrCreate(
|
||||
['key' => $key],
|
||||
['value' => $request->currency]
|
||||
);
|
||||
|
||||
Cache::forget($key);
|
||||
|
||||
return response()->json(__('Currency setting updated successfully.'));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Option;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BusinessInvoiceSettingController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
|
||||
$invoiceSetting = Option::where('key', 'invoice_settings')
|
||||
->where('value', 'LIKE', '%"business_id":%' . $businessId . '%')
|
||||
->get()
|
||||
->firstWhere('value.business_id', $businessId);
|
||||
|
||||
if ($invoiceSetting && isset($invoiceSetting->value['invoice_size'])) {
|
||||
return response()->json([
|
||||
'message' => __('Invoice size fetched successfully.'),
|
||||
'invoice_size' => $invoiceSetting->value['invoice_size'],
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => __('Invoice size not found.'),
|
||||
'invoice_size' => null,
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
|
||||
public function updateInvoice(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'invoice_size' => 'required|string|max:100|in:a4,3_inch_80mm,2_inch_58mm',
|
||||
]);
|
||||
|
||||
$key = 'invoice_setting_' . auth()->user()->business_id;
|
||||
|
||||
Option::updateOrCreate(
|
||||
['key' => $key],
|
||||
['value' => $request->invoice_size]
|
||||
);
|
||||
|
||||
Cache::forget($key);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Invoice size updated successfully.'),
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
307
app/Http/Controllers/Api/CashController.php
Normal file
307
app/Http/Controllers/Api/CashController.php
Normal file
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\HasUploader;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class CashController extends Controller
|
||||
{
|
||||
use HasUploader;
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$data = Transaction::with('user:id,name')
|
||||
->where('business_id', $business_id)
|
||||
->whereIn('transaction_type', ['cash_payment', 'bank_to_cash', 'cash_to_bank', 'adjust_cash', 'cheque_to_cash'])
|
||||
->when($request->duration, function ($query) use ($request) {
|
||||
$today = Carbon::today();
|
||||
|
||||
if ($request->duration === 'today') {
|
||||
$query->whereDate('date', $today);
|
||||
} elseif ($request->duration === 'yesterday') {
|
||||
$query->whereDate('date', Carbon::yesterday());
|
||||
} elseif ($request->duration === 'last_seven_days') {
|
||||
$startDate = Carbon::now()->subDays(7)->format('Y-m-d');
|
||||
$endDate = Carbon::now()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === 'last_thirty_days') {
|
||||
$startDate = Carbon::now()->subDays(30)->format('Y-m-d');
|
||||
$endDate = Carbon::now()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === 'current_month') {
|
||||
$startDate = Carbon::now()->startOfMonth()->format('Y-m-d');
|
||||
$endDate = Carbon::now()->endOfMonth()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === 'last_month') {
|
||||
$startDate = Carbon::now()->subMonth()->startOfMonth()->format('Y-m-d');
|
||||
$endDate = Carbon::now()->subMonth()->endOfMonth()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === 'current_year') {
|
||||
$startDate = Carbon::now()->startOfYear()->format('Y-m-d');
|
||||
$endDate = Carbon::now()->endOfYear()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === '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');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
}
|
||||
})
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$total_balance = cash_balance();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
'total_balance' => $total_balance,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$transaction = Transaction::with('user:id,name', 'branch:id,name')
|
||||
->where('business_id', $business_id)
|
||||
->where('id', $id)
|
||||
->first();
|
||||
|
||||
if (!$transaction) {
|
||||
return response()->json([
|
||||
'message' => __('Transaction not found.'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Transaction fetched successfully.'),
|
||||
'data' => $transaction,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'to' => 'nullable|exists:payment_types,id',
|
||||
'type' => 'nullable|in:credit,debit',
|
||||
'transaction_type' => 'required|in:cash_to_bank,adjust_cash',
|
||||
'amount' => 'required|numeric|min:0.01',
|
||||
'date' => 'nullable|date',
|
||||
'image' => 'nullable|image|mimes:jpg,png,jpeg,svg',
|
||||
'note' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$business_id = auth()->user()->business_id;
|
||||
$amount = $request->amount ?? 0;
|
||||
$type = 'transfer';
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Cash to Bank
|
||||
if ($request->transaction_type === 'cash_to_bank') {
|
||||
$toBank = PaymentType::findOrFail($request->to);
|
||||
// increase target bank balance
|
||||
$toBank->increment('balance', $amount);
|
||||
|
||||
// Adjust Cash
|
||||
} elseif ($request->transaction_type === 'adjust_cash') {
|
||||
$type = $request->type;
|
||||
}
|
||||
|
||||
// Store transaction record
|
||||
$data = Transaction::create([
|
||||
'business_id' => $business_id,
|
||||
'user_id' => auth()->id(),
|
||||
'type' => $type,
|
||||
'platform' => 'cash',
|
||||
'transaction_type' => $request->transaction_type,
|
||||
'amount' => $amount,
|
||||
'from_bank' => null,
|
||||
'to_bank' => ($request->transaction_type === 'cash_to_bank') ? $request->to : null,
|
||||
'date' => $request->date ?? now(),
|
||||
'image' => $request->image ? $this->upload($request, 'image') : NULL,
|
||||
'note' => $request->note,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json([
|
||||
'message' => 'Error: ' . $e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'to' => 'nullable|exists:payment_types,id',
|
||||
'type' => 'nullable|in:credit,debit',
|
||||
'transaction_type' => 'required|in:cash_to_bank,adjust_cash',
|
||||
'amount' => 'required|numeric|min:0.01',
|
||||
'date' => 'nullable|date',
|
||||
'image' => 'nullable|image|mimes:jpg,png,jpeg,svg',
|
||||
'note' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$transaction = Transaction::findOrFail($id);
|
||||
$newAmount = $request->amount ?? 0;
|
||||
$newTransactionType = $request->transaction_type;
|
||||
$type = 'transfer';
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Transaction type is the same
|
||||
if ($transaction->transaction_type === $newTransactionType) {
|
||||
if ($newTransactionType === 'cash_to_bank') {
|
||||
$toBank = PaymentType::findOrFail($request->to);
|
||||
|
||||
// Adjust balance difference
|
||||
$diff = $newAmount - $transaction->amount;
|
||||
|
||||
// Prevent negative balance
|
||||
if ($toBank->balance + $diff < 0) {
|
||||
return response()->json([
|
||||
'message' => 'Cannot update: updated bank balance would be negative.'
|
||||
], 400);
|
||||
}
|
||||
|
||||
$toBank->increment('balance', $diff);
|
||||
|
||||
} elseif ($newTransactionType === 'adjust_cash') {
|
||||
$type = $request->type;
|
||||
}
|
||||
}
|
||||
// Transaction type changed
|
||||
else {
|
||||
// Reverse old transaction effect
|
||||
if ($transaction->transaction_type === 'cash_to_bank' && $transaction->to_bank) {
|
||||
$prevBank = PaymentType::find($transaction->to_bank);
|
||||
if ($prevBank) {
|
||||
if ($prevBank->balance < $transaction->amount) {
|
||||
return response()->json([
|
||||
'message' => 'Cannot update: insufficient balance in ' . $prevBank->name . ' to reverse previous transaction.'
|
||||
], 400);
|
||||
}
|
||||
$prevBank->decrement('balance', $transaction->amount);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply new transaction effect
|
||||
if ($newTransactionType === 'cash_to_bank') {
|
||||
$toBank = PaymentType::findOrFail($request->to);
|
||||
$toBank->increment('balance', $newAmount);
|
||||
$type = 'transfer';
|
||||
} elseif ($newTransactionType === 'adjust_cash') {
|
||||
$type = $request->type;
|
||||
}
|
||||
}
|
||||
|
||||
// Update transaction record
|
||||
$transaction->update([
|
||||
'type' => $type,
|
||||
'transaction_type' => $newTransactionType,
|
||||
'amount' => $newAmount,
|
||||
'to_bank' => ($newTransactionType === 'cash_to_bank') ? $request->to : null,
|
||||
'date' => $request->date ?? now(),
|
||||
'image' => $request->image ? $this->upload($request, 'image') : $transaction->image,
|
||||
'note' => $request->note,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $transaction,
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json([
|
||||
'message' => 'Error: ' . $e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
|
||||
$transaction = Transaction::findOrFail($id);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Allow only "cash" platform transactions to be deleted
|
||||
if ($transaction->platform !== 'cash') {
|
||||
return response()->json([
|
||||
'message' => 'Cannot delete here, please delete from ' . ucfirst($transaction->platform) . ' section.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
$amount = $transaction->amount;
|
||||
$toBank = $transaction->to_bank ? PaymentType::find($transaction->to_bank) : null;
|
||||
|
||||
// Reverse balance changes based on transaction type
|
||||
switch ($transaction->transaction_type) {
|
||||
case 'cash_to_bank':
|
||||
if ($toBank) {
|
||||
// Ensure bank has enough balance to reverse
|
||||
if ($toBank->balance < $amount) {
|
||||
return response()->json([
|
||||
'message' => 'Cannot delete: bank balance would go negative.',
|
||||
], 400);
|
||||
}
|
||||
$toBank->decrement('balance', $amount);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'adjust_cash':
|
||||
// Cash is static, so no bank adjustments needed
|
||||
break;
|
||||
}
|
||||
|
||||
if (file_exists($transaction->image)) {
|
||||
Storage::delete($transaction->image);
|
||||
}
|
||||
|
||||
$transaction->delete();
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json([
|
||||
'message' => 'Error: ' . $e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
297
app/Http/Controllers/Api/ChequeController.php
Normal file
297
app/Http/Controllers/Api/ChequeController.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Income;
|
||||
use App\Models\Party;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\Sale;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ChequeController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$data = Transaction::with(['user:id,name','paymentType:id,name'])
|
||||
->where('business_id', $business_id)
|
||||
->whereIn('transaction_type', ['cheque_payment'])
|
||||
->when($request->duration, function ($query) use ($request) {
|
||||
$today = Carbon::today();
|
||||
|
||||
if ($request->duration === 'today') {
|
||||
$query->whereDate('date', $today);
|
||||
} elseif ($request->duration === 'yesterday') {
|
||||
$query->whereDate('date', Carbon::yesterday());
|
||||
} elseif ($request->duration === 'last_seven_days') {
|
||||
$startDate = Carbon::now()->subDays(7)->format('Y-m-d');
|
||||
$endDate = Carbon::now()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === 'last_thirty_days') {
|
||||
$startDate = Carbon::now()->subDays(30)->format('Y-m-d');
|
||||
$endDate = Carbon::now()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === 'current_month') {
|
||||
$startDate = Carbon::now()->startOfMonth()->format('Y-m-d');
|
||||
$endDate = Carbon::now()->endOfMonth()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === 'last_month') {
|
||||
$startDate = Carbon::now()->subMonth()->startOfMonth()->format('Y-m-d');
|
||||
$endDate = Carbon::now()->subMonth()->endOfMonth()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === 'current_year') {
|
||||
$startDate = Carbon::now()->startOfYear()->format('Y-m-d');
|
||||
$endDate = Carbon::now()->endOfYear()->format('Y-m-d');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
} elseif ($request->duration === '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');
|
||||
$query->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate);
|
||||
}
|
||||
})
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'transaction_id' => 'required|exists:transactions,id',
|
||||
'date' => 'nullable|date',
|
||||
'note' => 'nullable|string',
|
||||
'payment_type' => [
|
||||
'required',
|
||||
function ($attribute, $value, $fail) {
|
||||
if ($value !== 'cash' && !PaymentType::where('id', $value)->exists()) {
|
||||
$fail(__('The selected payment type is invalid.'));
|
||||
}
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$transaction = Transaction::findOrFail($request->transaction_id);
|
||||
$amount = $transaction->amount;
|
||||
$platform = strtolower($transaction->platform);
|
||||
|
||||
$transaction->update([
|
||||
'type' => 'deposit',
|
||||
]);
|
||||
|
||||
$newTransactionData = [
|
||||
'business_id' => $business_id,
|
||||
'user_id' => auth()->id(),
|
||||
'type' => 'credit',
|
||||
'platform' => 'cheque',
|
||||
'amount' => $amount,
|
||||
'date' => $request->date ?? now(),
|
||||
'note' => $request->note,
|
||||
'meta' => [
|
||||
'source_transaction_id' => $request->transaction_id ?? null,
|
||||
],
|
||||
];
|
||||
|
||||
// Cheque deposited into Cash
|
||||
if ($request->payment_type === 'cash') {
|
||||
$newTransactionData['transaction_type'] = 'cheque_to_cash';
|
||||
$newTransactionData['payment_type_id'] = null;
|
||||
} else {
|
||||
// Cheque deposited into Bank
|
||||
$toBank = PaymentType::findOrFail($request->payment_type);
|
||||
$toBank->increment('balance', $amount);
|
||||
|
||||
$newTransactionData['transaction_type'] = 'cheque_to_bank';
|
||||
$newTransactionData['payment_type_id'] = $toBank->id;
|
||||
}
|
||||
|
||||
Transaction::create($newTransactionData);
|
||||
|
||||
// update related platform
|
||||
if ($platform == 'sale') {
|
||||
$sale = Sale::find($transaction->reference_id);
|
||||
|
||||
updateBalance($amount, 'increment');
|
||||
|
||||
$newPaid = $sale->paidAmount + $amount;
|
||||
$expectedTotal = $sale->paidAmount + $sale->dueAmount;
|
||||
$newChange = max($newPaid - $expectedTotal, 0);
|
||||
$newDue = max($sale->dueAmount - $amount, 0);
|
||||
|
||||
$sale->update([
|
||||
'paidAmount' => $newPaid,
|
||||
'change_amount' => $sale->change_amount + $newChange,
|
||||
'dueAmount' => $newDue,
|
||||
'isPaid' => $newDue > 0 ? 0 : 1,
|
||||
]);
|
||||
|
||||
// update party due
|
||||
if ($sale->party_id) {
|
||||
$party = Party::find($sale->party_id);
|
||||
if ($party) {
|
||||
$party->decrement('due', min($amount, $party->due));
|
||||
}
|
||||
}
|
||||
} elseif ($transaction->platform == 'income') {
|
||||
$income = Income::find($transaction->reference_id);
|
||||
$income->increment('amount', $transaction->amount);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $transaction
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json([
|
||||
'message' => $e->getMessage()
|
||||
], 406);
|
||||
}
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$transaction = Transaction::with('user:id,name', 'branch:id,name')
|
||||
->where('business_id', $business_id)
|
||||
->where('id', $id)
|
||||
->first();
|
||||
|
||||
if (!$transaction) {
|
||||
return response()->json([
|
||||
'message' => __('Transaction not found.'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Transaction fetched successfully.'),
|
||||
'data' => $transaction,
|
||||
]);
|
||||
}
|
||||
|
||||
public function reopen($TransactionId)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// original cheque payment
|
||||
$originalTxn = Transaction::findOrFail($TransactionId);
|
||||
|
||||
// Must be cheque_payment
|
||||
if ($originalTxn->transaction_type !== 'cheque_payment') {
|
||||
return response()->json(['message' => __('This transaction cannot be reopened.')], 400);
|
||||
}
|
||||
|
||||
$amount = $originalTxn->amount;
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$depositTxn = Transaction::where('platform', 'cheque')
|
||||
->where('meta->source_transaction_id', $originalTxn->id)
|
||||
->first();
|
||||
|
||||
if (!$depositTxn) {
|
||||
return response()->json(['message' => __('Deposited cheque record not found.')], 400);
|
||||
}
|
||||
|
||||
// Reverse bank balance only if cheque_to_bank
|
||||
if ($depositTxn->transaction_type === 'cheque_to_bank' && $depositTxn->payment_type_id) {
|
||||
$bank = PaymentType::find($depositTxn->payment_type_id);
|
||||
if ($bank) {
|
||||
$bank->decrement('balance', $amount);
|
||||
}
|
||||
}
|
||||
|
||||
$platform = strtolower($originalTxn->platform);
|
||||
|
||||
// Reverse Sale or Income
|
||||
if ($platform == 'sale') {
|
||||
$sale = Sale::find($originalTxn->reference_id);
|
||||
if ($sale) {
|
||||
// Reverse paid, change, and due amounts
|
||||
$newPaid = $sale->paidAmount - $amount;
|
||||
$newDue = $sale->dueAmount + $amount;
|
||||
$newChange = max($sale->change_amount - max($sale->paidAmount - ($sale->paidAmount + $sale->dueAmount - $amount), 0), 0);
|
||||
|
||||
$sale->update([
|
||||
'paidAmount' => max($newPaid, 0),
|
||||
'change_amount' => max($newChange, 0),
|
||||
'dueAmount' => $newDue,
|
||||
'isPaid' => $newDue > 0 ? 0 : 1,
|
||||
]);
|
||||
|
||||
// Reverse party due if exists
|
||||
if ($sale->party_id) {
|
||||
$party = Party::find($sale->party_id);
|
||||
if ($party) {
|
||||
$party->increment('due', $amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($platform == 'income') {
|
||||
$income = Income::find($originalTxn->reference_id);
|
||||
if ($income) {
|
||||
$income->decrement('amount', $amount);
|
||||
}
|
||||
}
|
||||
|
||||
$reverseType = $depositTxn->transaction_type === 'cheque_to_cash' ? 'cash_to_cheque' : 'bank_to_cheque';
|
||||
|
||||
// Create reverse debit transaction
|
||||
Transaction::create([
|
||||
'business_id' => $business_id,
|
||||
'user_id' => auth()->id(),
|
||||
'type' => 'debit',
|
||||
'platform' => 'cheque',
|
||||
'amount' => $amount,
|
||||
'date' => now(),
|
||||
'note' => 'Cheque reopened',
|
||||
'transaction_type' => $reverseType,
|
||||
'payment_type_id' => $depositTxn->payment_type_id,
|
||||
'meta' => [
|
||||
'reverted_transaction_id' => $depositTxn->id
|
||||
]
|
||||
]);
|
||||
|
||||
$originalTxn->update([
|
||||
'type' => 'pending',
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $originalTxn
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
], 406);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
27
app/Http/Controllers/Api/DefaultLangController.php
Normal file
27
app/Http/Controllers/Api/DefaultLangController.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Party;
|
||||
use App\Models\Business;
|
||||
use App\Helpers\HasUploader;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\PartyLedgerService;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DefaultLangController extends Controller
|
||||
{
|
||||
use HasUploader;
|
||||
|
||||
public function index()
|
||||
{
|
||||
$default_lang = get_option('general')['default_lang'] ?? '';
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $default_lang,
|
||||
]);
|
||||
}
|
||||
}
|
||||
79
app/Http/Controllers/Api/ExpenseCategoryController.php
Normal file
79
app/Http/Controllers/Api/ExpenseCategoryController.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ExpenseCategory;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ExpenseCategoryController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data = ExpenseCategory::where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'categoryName' => 'required|unique:expense_categories,categoryName,NULL,id,business_id,' . auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
$data = ExpenseCategory::create($request->except('status') + [
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'status' => $request->status == 'true' ? 1 : 0,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$category = ExpenseCategory::findOrFail($id);
|
||||
|
||||
$request->validate([
|
||||
'categoryName' => [
|
||||
'required',
|
||||
'unique:expense_categories,categoryName,' . $category->id . ',id,business_id,' . auth()->user()->business_id,
|
||||
],
|
||||
]);
|
||||
|
||||
$category->update($request->except('status') + [
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'status' => $request->status == 'true' ? 1 : 0,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$category = ExpenseCategory::findOrFail($id);
|
||||
$category->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
318
app/Http/Controllers/Api/PartyController.php
Normal file
318
app/Http/Controllers/Api/PartyController.php
Normal file
@@ -0,0 +1,318 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Party;
|
||||
use App\Models\Business;
|
||||
use App\Helpers\HasUploader;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\PartyLedgerService;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PartyController extends Controller
|
||||
{
|
||||
use HasUploader;
|
||||
|
||||
public function index()
|
||||
{
|
||||
$user = auth()->user();
|
||||
$businessId = $user->business_id;
|
||||
$activeBranch = $user->active_branch;
|
||||
|
||||
$parties = Party::where('business_id', $businessId)
|
||||
->when(request('type'), function ($q) {
|
||||
$q->where('type', request('type'));
|
||||
})
|
||||
->withCount([
|
||||
'sales as sales_count' => function ($q) use ($activeBranch) {
|
||||
if (moduleCheck('MultiBranchAddon') && $activeBranch) {
|
||||
$q->where('branch_id', $activeBranch->id);
|
||||
}
|
||||
}
|
||||
])
|
||||
->withSum([
|
||||
'sales as total_sale_amount' => function ($q) use ($activeBranch) {
|
||||
if (moduleCheck('MultiBranchAddon') && $activeBranch) {
|
||||
$q->where('branch_id', $activeBranch->id);
|
||||
}
|
||||
}
|
||||
], 'totalAmount')
|
||||
->withSum([
|
||||
'sales as total_sale_paid' => function ($q) use ($activeBranch) {
|
||||
if (moduleCheck('MultiBranchAddon') && $activeBranch) {
|
||||
$q->where('branch_id', $activeBranch->id);
|
||||
}
|
||||
}
|
||||
], 'paidAmount')
|
||||
->withSum([
|
||||
'sales as total_sale_profit' => function ($q) use ($activeBranch) {
|
||||
$q->where('lossProfit', '>=', 0);
|
||||
if (moduleCheck('MultiBranchAddon') && $activeBranch) {
|
||||
$q->where('branch_id', $activeBranch->id);
|
||||
}
|
||||
}
|
||||
], 'lossProfit')
|
||||
->withSum([
|
||||
'sales as total_sale_loss' => function ($q) use ($activeBranch) {
|
||||
$q->where('lossProfit', '<', 0);
|
||||
if (moduleCheck('MultiBranchAddon') && $activeBranch) {
|
||||
$q->where('branch_id', $activeBranch->id);
|
||||
}
|
||||
}
|
||||
], 'lossProfit')
|
||||
->withCount([
|
||||
'purchases as purchases_count' => function ($q) use ($activeBranch) {
|
||||
if (moduleCheck('MultiBranchAddon') && $activeBranch) {
|
||||
$q->where('branch_id', $activeBranch->id);
|
||||
}
|
||||
}
|
||||
])
|
||||
->withSum([
|
||||
'purchases as total_purchase_amount' => function ($q) use ($activeBranch) {
|
||||
if (moduleCheck('MultiBranchAddon') && $activeBranch) {
|
||||
$q->where('branch_id', $activeBranch->id);
|
||||
}
|
||||
}
|
||||
], 'totalAmount')
|
||||
->withSum([
|
||||
'purchases as total_purchase_paid' => function ($q) use ($activeBranch) {
|
||||
if (moduleCheck('MultiBranchAddon') && $activeBranch) {
|
||||
$q->where('branch_id', $activeBranch->id);
|
||||
}
|
||||
}
|
||||
], 'paidAmount')
|
||||
->with([
|
||||
'sales' => function ($sq) use ($activeBranch) {
|
||||
$sq->select('id', 'party_id', 'branch_id');
|
||||
|
||||
if (moduleCheck('MultiBranchAddon') && $activeBranch) {
|
||||
$sq->where('branch_id', $activeBranch->id);
|
||||
}
|
||||
|
||||
$sq->with([
|
||||
'details' => function ($dq) {
|
||||
$dq->select(
|
||||
'id',
|
||||
'sale_id',
|
||||
'product_id',
|
||||
'quantities',
|
||||
'productPurchasePrice',
|
||||
'price',
|
||||
'lossProfit'
|
||||
)->with([
|
||||
'product:id,productName'
|
||||
]);
|
||||
}
|
||||
]);
|
||||
}
|
||||
])
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$accessToMultiBranch = auth()->user()->accessToMultiBranch();
|
||||
|
||||
foreach ($parties as $party) {
|
||||
$branch_logic = $party->branch_id === ($activeBranch->id ?? false) || $accessToMultiBranch;
|
||||
|
||||
if ($activeBranch && moduleCheck('MultiBranchAddon')) {
|
||||
if ($party->type === 'Supplier') {
|
||||
if (!$branch_logic) {
|
||||
$party->due = $party->purchases_dues
|
||||
->where('branch_id', $activeBranch->id)
|
||||
->sum('dueAmount');
|
||||
}
|
||||
} else {
|
||||
if (!$branch_logic) {
|
||||
$party->due = $party->sales_dues
|
||||
->where('branch_id', $activeBranch->id)
|
||||
->sum('dueAmount');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$party->wallet = $branch_logic ? $party->wallet : 0;
|
||||
$party->opening_balance = $branch_logic ? $party->opening_balance : 0;
|
||||
$party->opening_balance_type = $branch_logic ? $party->opening_balance_type : null;
|
||||
|
||||
if ($party->type === 'Supplier') {
|
||||
$party->makeHidden([
|
||||
'sales_count',
|
||||
'total_sale_amount',
|
||||
'total_sale_paid',
|
||||
'total_sale_profit',
|
||||
'total_sale_loss',
|
||||
]);
|
||||
} else {
|
||||
$party->makeHidden([
|
||||
'purchases_count',
|
||||
'total_purchase_amount',
|
||||
'total_purchase_paid',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $parties,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'type' => 'required|string|in:Retailer,Dealer,Wholesaler,Supplier',
|
||||
'phone' => 'nullable|max:20|' . Rule::unique('parties')->where('business_id', $business_id),
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg,svg',
|
||||
'address' => 'nullable|string|max:255',
|
||||
'credit_limit' => 'nullable|numeric|min:0|max:999999999999.99',
|
||||
'opening_balance' => 'nullable|numeric|min:-999999999999.99|max:999999999999.99',
|
||||
'opening_balance_type' => 'required|in:due,advance',
|
||||
]);
|
||||
|
||||
$data = Party::create($request->except('image', 'due', 'wallet', 'opening_balance', 'credit_limit') + [
|
||||
'due' => ($request->opening_balance_type == 'due') ? ($request->opening_balance ?? 0) : 0,
|
||||
'wallet' => ($request->opening_balance_type == 'advance') ? ($request->opening_balance ?? 0) : 0,
|
||||
'opening_balance' => $request->opening_balance ?? 0,
|
||||
'credit_limit' => $request->credit_limit ?? 0,
|
||||
'image' => $request->image ? $this->upload($request, 'image') : NULL,
|
||||
'business_id' => $business_id
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(Party $party)
|
||||
{
|
||||
if (env('MESSAGE_ENABLED')) {
|
||||
if ($party->due) {
|
||||
$business = Business::findOrFail($party->business_id);
|
||||
$response = sendMessage($party->phone, dueMessage($party, $business->companyName));
|
||||
|
||||
if ($response->successful()) {
|
||||
return response()->json([
|
||||
'message' => __('Message has been send successfully.'),
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Something went wrong, Please contact with admin.'),
|
||||
], 406);
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => __('This party has no due balance.'),
|
||||
], 406);
|
||||
}
|
||||
} else {
|
||||
return response()->json([
|
||||
'message' => __('Message has been disabled by admin.'),
|
||||
], 406);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Party $party)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'type' => 'required|string|in:Retailer,Dealer,Wholesaler,Supplier',
|
||||
'phone' => 'nullable|max:20|unique:parties,phone,' . $party->id . ',id,business_id,' . auth()->user()->business_id,
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg,svg',
|
||||
'address' => 'nullable|string|max:255',
|
||||
'credit_limit' => 'nullable|numeric|min:0|max:999999999999.99',
|
||||
'opening_balance' => 'nullable|numeric|min:-999999999999.99|max:999999999999.99',
|
||||
'opening_balance_type' => 'required|in:due,advance',
|
||||
]);
|
||||
|
||||
$branch_logic = $party->branch_id == auth()->user()->active_branch?->id;
|
||||
|
||||
// Previous
|
||||
$prevOpening = $party->opening_balance ?? 0;
|
||||
$prevType = $party->opening_balance_type;
|
||||
|
||||
// Current
|
||||
$currentOpening = $request->opening_balance ?? 0;
|
||||
$currentType = $request->opening_balance_type;
|
||||
|
||||
// Start with existing balance
|
||||
$due = $party->due;
|
||||
$wallet = $party->wallet;
|
||||
|
||||
if ($prevType == $currentType) {
|
||||
// Same type → adjust by difference
|
||||
if ($currentType == 'due') {
|
||||
$due += ($currentOpening - $prevOpening);
|
||||
} else {
|
||||
$wallet += ($currentOpening - $prevOpening);
|
||||
}
|
||||
} else {
|
||||
// Type changed → shift balances
|
||||
if ($prevType == 'due' && $currentType == 'advance') {
|
||||
$due -= $prevOpening;
|
||||
$wallet += $currentOpening;
|
||||
} elseif ($prevType == 'advance' && $currentType == 'due') {
|
||||
$wallet -= $prevOpening;
|
||||
$due += $currentOpening;
|
||||
}
|
||||
}
|
||||
|
||||
$party->update(
|
||||
$request->except('image', 'due', 'wallet', 'opening_balance', 'credit_limit', 'business_id') + [
|
||||
'due' => $branch_logic ? $due : $party->due,
|
||||
'wallet' => $branch_logic ? $wallet : $party->wallet,
|
||||
'opening_balance' => $currentOpening,
|
||||
'opening_balance_type' => $currentType,
|
||||
'credit_limit' => $request->credit_limit ?? $party->credit_limit,
|
||||
'image' => $request->image ? $this->upload($request, 'image', $party->image) : $party->image,
|
||||
]
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $party,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Party $party)
|
||||
{
|
||||
if (!$party->canBeDeleted()) {
|
||||
return response()->json([
|
||||
'message' => __('This party cannot be deleted.'),
|
||||
], 400);
|
||||
}
|
||||
|
||||
if (file_exists($party->image)) {
|
||||
Storage::delete($party->image);
|
||||
}
|
||||
|
||||
$party->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function partyLedger(Request $request, $partyId, PartyLedgerService $service)
|
||||
{
|
||||
$ledger = $service->list($request, $partyId);
|
||||
|
||||
return response()->json([
|
||||
'data' => $ledger,
|
||||
]);
|
||||
}
|
||||
}
|
||||
69
app/Http/Controllers/Api/PaymentTypeController.php
Normal file
69
app/Http/Controllers/Api/PaymentTypeController.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PaymentType;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PaymentTypeController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = PaymentType::where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => [
|
||||
'required',
|
||||
Rule::unique('payment_types')->where(function ($query) {
|
||||
return $query->where('business_id', auth()->user()->business_id);
|
||||
}),
|
||||
],
|
||||
]);
|
||||
|
||||
$data = PaymentType::create($request->all() + [
|
||||
'business_id' => auth()->user()->business_id
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
$payment_type = PaymentType::findOrFail($id);
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|unique:payment_types,name,' . $payment_type->id . ',id,business_id,' . auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
$payment_type->update($request->all());
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $payment_type,
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
$payment_type = PaymentType::findOrFail($id);
|
||||
$payment_type->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
79
app/Http/Controllers/Api/ProducModelController.php
Normal file
79
app/Http/Controllers/Api/ProducModelController.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ProductModel;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProducModelController extends Controller
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = ProductModel::where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|unique:product_models,name,NULL,id,business_id,' . auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
$data = ProductModel::create($request->all() + [
|
||||
'business_id' => auth()->user()->business_id
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
$product_model = ProductModel::find($id);
|
||||
if (!$product_model) {
|
||||
return response()->json([
|
||||
'message' => __('Model not found.'),
|
||||
'data' => null,
|
||||
], 404);
|
||||
}
|
||||
$request->validate([
|
||||
'name' => [
|
||||
'required',
|
||||
'unique:product_models,name,' . $product_model->id . ',id,business_id,' . auth()->user()->business_id,
|
||||
],
|
||||
]);
|
||||
|
||||
$product_model->update($request->all());
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $product_model,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
$product_model = ProductModel::find($id);
|
||||
|
||||
if (!$product_model) {
|
||||
return response()->json([
|
||||
'message' => __('Model not found.'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
$product_model->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
142
app/Http/Controllers/Api/ProductSettingsController.php
Normal file
142
app/Http/Controllers/Api/ProductSettingsController.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ProductSetting;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProductSettingsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data = ProductSetting::where('business_id', auth()->user()->business_id)->first();
|
||||
|
||||
if ($data) {
|
||||
$responseData = $data;
|
||||
} else {
|
||||
$responseData = [
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'modules' => [
|
||||
// Default fields = "1"
|
||||
'show_product_type_single' => "1",
|
||||
'show_product_category' => "1",
|
||||
'show_alert_qty' => "1",
|
||||
'show_product_unit' => "1",
|
||||
'show_exclusive_price' => "1",
|
||||
'show_inclusive_price' => "1",
|
||||
'show_profit_percent' => "1",
|
||||
'show_product_sale_price' => "1",
|
||||
'show_product_price' => "1",
|
||||
'show_product_stock' => "1",
|
||||
|
||||
'default_sale_price' => null,
|
||||
'default_wholesale_price' => null,
|
||||
'default_dealer_price' => null,
|
||||
'default_batch_no' => null,
|
||||
'expire_date_type' => null,
|
||||
'mfg_date_type' => null,
|
||||
'default_expired_date' => null,
|
||||
'default_mfg_date' => null,
|
||||
|
||||
'show_product_code' => "0",
|
||||
'show_product_brand' => "0",
|
||||
'show_model_no' => "0",
|
||||
'show_product_manufacturer' => "0",
|
||||
'show_product_image' => "0",
|
||||
'show_vat_id' => "0",
|
||||
'show_vat_type' => "0",
|
||||
'show_action' => "0",
|
||||
'show_product_dealer_price' => "0",
|
||||
'show_product_wholesale_price' => "0",
|
||||
'show_batch_no' => "0",
|
||||
'show_expire_date' => "0",
|
||||
'show_mfg_date' => "0",
|
||||
'show_product_type_variant' => "0",
|
||||
'show_product_batch_no' => "0",
|
||||
'show_product_expire_date' => "0",
|
||||
'show_product_type_combo' => "0",
|
||||
'show_warehouse' => "0",
|
||||
'show_rack' => "0",
|
||||
'show_shelf' => "0",
|
||||
'show_guarantee' => "0",
|
||||
'show_warranty' => "0",
|
||||
'show_serial' => "0",
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $responseData,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'show_product_name' => 'nullable|integer',
|
||||
'show_product_price' => 'nullable|integer',
|
||||
'show_product_code' => 'nullable|integer',
|
||||
'show_product_stock' => 'nullable|integer',
|
||||
'show_product_sale_price' => 'nullable|integer',
|
||||
'show_product_dealer_price' => 'nullable|integer',
|
||||
'show_product_wholesale_price' => 'nullable|integer',
|
||||
'show_product_unit' => 'nullable|integer',
|
||||
'show_product_brand' => 'nullable|integer',
|
||||
'show_product_category' => 'nullable|integer',
|
||||
'show_product_manufacturer' => 'nullable|integer',
|
||||
'show_product_image' => 'nullable|integer',
|
||||
'show_expire_date' => 'nullable|integer',
|
||||
'show_alert_qty' => 'nullable|integer',
|
||||
'show_vat_id' => 'nullable|integer',
|
||||
'show_vat_type' => 'nullable|integer',
|
||||
'show_exclusive_price' => 'nullable|integer',
|
||||
'show_inclusive_price' => 'nullable|integer',
|
||||
'show_profit_percent' => 'nullable|integer',
|
||||
'show_capacity' => 'nullable|integer',
|
||||
'show_weight' => 'nullable|integer',
|
||||
'show_color' => 'nullable|integer',
|
||||
'show_type' => 'nullable|integer',
|
||||
'show_size' => 'nullable|integer',
|
||||
'show_batch_no' => 'nullable|integer',
|
||||
'show_mfg_date' => 'nullable|integer',
|
||||
'show_model_no' => 'nullable|integer',
|
||||
'default_sale_price' => 'nullable|integer',
|
||||
'default_wholesale_price' => 'nullable|integer',
|
||||
'default_dealer_price' => 'nullable|integer',
|
||||
'show_product_type_single' => 'nullable|integer',
|
||||
'show_product_type_variant' => 'nullable|integer',
|
||||
'show_product_type_combo' => 'nullable|integer',
|
||||
'show_warehouse' => 'nullable|integer',
|
||||
'show_action' => 'nullable|integer',
|
||||
'show_rack' => 'nullable|integer',
|
||||
'show_shelf' => 'nullable|integer',
|
||||
'show_guarantee' => 'nullable|integer',
|
||||
'show_warranty' => 'nullable|integer',
|
||||
'show_serial' => 'nullable|integer',
|
||||
'default_batch_no' => 'nullable|integer',
|
||||
'default_expired_date' => 'nullable|integer',
|
||||
'default_mfg_date' => 'nullable|integer',
|
||||
'expire_date_type' => 'nullable|integer',
|
||||
'mfg_date_type' => 'nullable|integer',
|
||||
'show_product_batch_no' => 'nullable|integer',
|
||||
'show_product_expire_date' => 'nullable|integer'
|
||||
]);
|
||||
|
||||
ProductSetting::updateOrCreate(
|
||||
['business_id' => auth()->user()->business_id],
|
||||
['modules' => $request->all()]
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
416
app/Http/Controllers/Api/PurchaseController.php
Normal file
416
app/Http/Controllers/Api/PurchaseController.php
Normal file
@@ -0,0 +1,416 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Events\MultiPaymentProcessed;
|
||||
use App\Events\PurchaseSms;
|
||||
use App\Models\Party;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\Product;
|
||||
use App\Models\Stock;
|
||||
use App\Models\Purchase;
|
||||
use App\Models\Transaction;
|
||||
use App\Traits\DateFilterTrait;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\PurchaseDetails;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PurchaseReturn;
|
||||
|
||||
class PurchaseController extends Controller
|
||||
{
|
||||
Use DateFilterTrait;
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
// Build the query with all eager loads
|
||||
$query = Purchase::with(['user:id,name,role', 'party:id,name,email,phone,type,address', 'details', 'details.product:id,productName,category_id,product_type,vat_id,vat_type,vat_amount', 'details.stock:id,batch_no,variant_name,warehouse_id', 'details.product.vat:id,name,rate', 'details.product.category:id,categoryName', 'purchaseReturns.details', 'vat:id,name,rate', 'branch:id,name,phone,address','transactions:id,platform,transaction_type,amount,date,invoice_no,reference_id,payment_type_id,meta', 'transactions.paymentType:id,name'])->where('business_id', $business_id);
|
||||
|
||||
// Filter returned sales (safer boolean check)
|
||||
$query->when(request('returned-purchase') == "true", function ($query) {
|
||||
$query->whereHas('purchaseReturns');
|
||||
});
|
||||
|
||||
if ($request->filled('branch_id')) {
|
||||
$query->where('branch_id', $request->branch_id);
|
||||
}
|
||||
|
||||
// Apply date filter
|
||||
if(request('duration')){
|
||||
$this->applyDateFilter($query, request('duration'), 'purchaseDate', request('from_date'), request('to_date'));
|
||||
}
|
||||
|
||||
// Search filter
|
||||
if ($request->filled('search')) {
|
||||
$query->where(function ($q) use ($request) {
|
||||
$q->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 . '%');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Finally fetch the data (preserves original structure)
|
||||
$data = $query->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'total_amount' => $data->sum('totalAmount'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$purchase = Purchase::with(['user:id,name,role', 'party:id,name,email,phone,type,address', 'details', 'details.product:id,productName,category_id,product_type,vat_id,vat_type,vat_amount', 'details.stock:id,batch_no,variant_name,warehouse_id', 'details.product.vat:id,name,rate', 'details.product.category:id,categoryName', 'purchaseReturns.details', 'vat:id,name,rate', 'branch:id,name,phone,address','transactions:id,platform,transaction_type,amount,date,invoice_no,reference_id,payment_type_id,meta', 'transactions.paymentType:id,name'])
|
||||
->where('business_id', $business_id)
|
||||
->where('id', $id)
|
||||
->first();
|
||||
|
||||
if (!$purchase) {
|
||||
return response()->json([
|
||||
'message' => __('Purchase not found.'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Purchase fetched successfully.'),
|
||||
'data' => $purchase,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'products' => 'required|array',
|
||||
'products.*.product_id' => 'required|exists:products,id',
|
||||
'party_id' => 'required|exists:parties,id'
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
// Party due update
|
||||
if ($request->dueAmount > 0) {
|
||||
$party = Party::findOrFail($request->party_id);
|
||||
|
||||
// Check party credit limit
|
||||
$newTotalDue = $party->due + $request->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
|
||||
]);
|
||||
}
|
||||
|
||||
updateBalance($request->paidAmount, 'decrement');
|
||||
|
||||
$purchase = Purchase::create($request->all() + [
|
||||
'user_id' => auth()->id(),
|
||||
'business_id' => $business_id,
|
||||
]);
|
||||
|
||||
$purchaseDetails = [];
|
||||
foreach ($request->products as $key => $product_data) {
|
||||
|
||||
$batch_no = $product_data['batch_no'] ?? NULL;
|
||||
$variantName = $product_data['variant_name'] ?? null;
|
||||
$existingStock = Stock::where(['batch_no' => $batch_no, 'product_id' => $product_data['product_id']])
|
||||
->when($variantName, fn($q) => $q->where('variant_name', $variantName))
|
||||
->first();
|
||||
|
||||
// update or create stock
|
||||
$stock = Stock::updateOrCreate(
|
||||
[
|
||||
'batch_no' => $batch_no,
|
||||
'business_id' => $business_id,
|
||||
'product_id' => $product_data['product_id'],
|
||||
'variant_name' => $variantName
|
||||
],
|
||||
[
|
||||
'product_id' => $product_data['product_id'],
|
||||
'mfg_date' => $product_data['mfg_date'] ?? NULL,
|
||||
'expire_date' => $product_data['expire_date'] ?? NULL,
|
||||
'profit_percent' => $product_data['profit_percent'] ?? 0,
|
||||
'productSalePrice' => $product_data['productSalePrice'] ?? 0,
|
||||
'productDealerPrice' => $product_data['productDealerPrice'] ?? 0,
|
||||
'productPurchasePrice' => $product_data['productPurchasePrice'] ?? 0,
|
||||
'productWholeSalePrice' => $product_data['productWholeSalePrice'] ?? 0,
|
||||
'productStock' => ($product_data['quantities'] ?? 0) + ($existingStock->productStock ?? 0),
|
||||
'warehouse_id' => $product_data['warehouse_id'] ?? null,
|
||||
]
|
||||
);
|
||||
|
||||
$purchaseDetails[$key] = [
|
||||
'stock_id' => $stock->id,
|
||||
'purchase_id' => $purchase->id,
|
||||
'product_id' => $product_data['product_id'],
|
||||
'quantities' => $product_data['quantities'] ?? 0,
|
||||
'productSalePrice' => $product_data['productSalePrice'] ?? 0,
|
||||
'productDealerPrice' => $product_data['productDealerPrice'] ?? 0,
|
||||
'productPurchasePrice' => $product_data['productPurchasePrice'] ?? 0,
|
||||
'productWholeSalePrice' => $product_data['productWholeSalePrice'] ?? 0,
|
||||
'profit_percent' => $product_data['profit_percent'] ?? 0,
|
||||
'expire_date' => $product_data['expire_date'] ?? NULL,
|
||||
'mfg_date' => $product_data['mfg_date'] ?? NULL,
|
||||
];
|
||||
}
|
||||
|
||||
PurchaseDetails::insert($purchaseDetails);
|
||||
|
||||
// MultiPaymentProcessed Event
|
||||
event(new MultiPaymentProcessed(
|
||||
$request->payments ?? [],
|
||||
$purchase->id,
|
||||
'purchase',
|
||||
$request->paidAmount ?? 0,
|
||||
$request->party_id ?? null,
|
||||
));
|
||||
|
||||
// Send SMS
|
||||
event(new PurchaseSms($purchase));
|
||||
|
||||
DB::commit();
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $purchase->load('user:id,name,role', 'party:id,name,email,phone,type,address', 'details', 'details.stock:id,batch_no', 'details.product:id,productName,category_id,product_type', 'details.product.category:id,categoryName', 'purchaseReturns.details', 'vat:id,name,rate', 'payment_type:id,name', 'branch:id,name,phone,address'),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Purchase $purchase)
|
||||
{
|
||||
$request->validate([
|
||||
'products' => 'required|array',
|
||||
'products.*.product_id' => 'required|exists:products,id',
|
||||
'party_id' => 'required|exists:parties,id'
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
||||
$has_return = PurchaseReturn::where('purchase_id', $purchase->id)->count();
|
||||
|
||||
if ($has_return > 0) {
|
||||
return response()->json([
|
||||
'message' => __("You can not update this purchase because it has already been returned.")
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Revert previous stock changes
|
||||
foreach ($purchase->details as $detail) {
|
||||
Stock::where('id', $detail->stock_id)->decrement('productStock', $detail->quantities);
|
||||
}
|
||||
|
||||
// Delete existing purchase details
|
||||
$purchase->details()->delete();
|
||||
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
$purchaseDetails = [];
|
||||
foreach ($request->products as $key => $product_data) {
|
||||
|
||||
$batch_no = $product_data['batch_no'] ?? NULL;
|
||||
$variantName = $cartItem->options['variant_name'] ?? null;
|
||||
$existingStock = Stock::where(['batch_no' => $batch_no, 'product_id' => $product_data['product_id']])
|
||||
->when($variantName, fn($q) => $q->where('variant_name', $variantName))
|
||||
->first();
|
||||
|
||||
// update or create stock
|
||||
$stock = Stock::updateOrCreate(
|
||||
[
|
||||
'batch_no' => $batch_no,
|
||||
'business_id' => $business_id,
|
||||
'product_id' => $product_data['product_id'],
|
||||
'variant_name' => $variantName
|
||||
],
|
||||
[
|
||||
'product_id' => $product_data['product_id'],
|
||||
'mfg_date' => $product_data['mfg_date'] ?? NULL,
|
||||
'expire_date' => $product_data['expire_date'] ?? NULL,
|
||||
'profit_percent' => $product_data['profit_percent'] ?? 0,
|
||||
'productSalePrice' => $product_data['productSalePrice'] ?? 0,
|
||||
'productDealerPrice' => $product_data['productDealerPrice'] ?? 0,
|
||||
'productPurchasePrice' => $product_data['productPurchasePrice'] ?? 0,
|
||||
'productWholeSalePrice' => $product_data['productWholeSalePrice'] ?? 0,
|
||||
'productStock' => ($product_data['quantities'] ?? 0) + ($existingStock->productStock ?? 0),
|
||||
'warehouse_id' => $product_data['warehouse_id'] ?? null,
|
||||
]
|
||||
);
|
||||
|
||||
$purchaseDetails[$key] = [
|
||||
'stock_id' => $stock->id,
|
||||
'purchase_id' => $purchase->id,
|
||||
'product_id' => $product_data['product_id'],
|
||||
'quantities' => $product_data['quantities'] ?? 0,
|
||||
'productSalePrice' => $product_data['productSalePrice'] ?? 0,
|
||||
'productDealerPrice' => $product_data['productDealerPrice'] ?? 0,
|
||||
'productPurchasePrice' => $product_data['productPurchasePrice'] ?? 0,
|
||||
'productWholeSalePrice' => $product_data['productWholeSalePrice'] ?? 0,
|
||||
'profit_percent' => $product_data['profit_percent'] ?? 0,
|
||||
'expire_date' => $product_data['expire_date'] ?? NULL,
|
||||
'mfg_date' => $product_data['mfg_date'] ?? NULL,
|
||||
];
|
||||
}
|
||||
|
||||
PurchaseDetails::insert($purchaseDetails);
|
||||
|
||||
if ($purchase->dueAmount || $request->dueAmount) {
|
||||
$party = Party::findOrFail($request->party_id);
|
||||
|
||||
// Calculate new due for this party
|
||||
$newDue = $request->party_id == $purchase->party_id ? (($party->due - $purchase->dueAmount) + $request->dueAmount) : ($party->due + $request->dueAmount);
|
||||
|
||||
// Check credit limit
|
||||
if ($party->credit_limit > 0 && $newDue > $party->credit_limit) {
|
||||
return response()->json([
|
||||
'message' => __('Cannot update purchase. Party due will exceed credit limit!')
|
||||
], 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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$balanceDiff = ($purchase->paidAmount ?? 0) - $request->paidAmount;
|
||||
updateBalance($balanceDiff, 'decrement');
|
||||
|
||||
$purchase->update($request->all() + [
|
||||
'user_id' => auth()->id(),
|
||||
]);
|
||||
|
||||
// Multiple Payment Process
|
||||
$oldTransactions = Transaction::where('business_id', $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', $business_id)
|
||||
->where('reference_id', $purchase->id)
|
||||
->where('platform', 'purchase')
|
||||
->delete();
|
||||
|
||||
event(new MultiPaymentProcessed(
|
||||
$request->payments ?? [],
|
||||
$purchase->id,
|
||||
'purchase',
|
||||
$request->paidAmount ?? 0,
|
||||
$request->party_id ?? null,
|
||||
));
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $purchase->load('user:id,name,role', 'party:id,name,email,phone,type,address', 'details', 'details.stock:id,batch_no', 'details.product:id,productName,category_id,product_type', 'details.product.category:id,categoryName', 'purchaseReturns.details', 'vat:id,name,rate', 'payment_type:id,name'),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(string $id)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$purchase = Purchase::with('details')->findOrFail($id);
|
||||
|
||||
$has_return = PurchaseReturn::where('purchase_id', $purchase->id)->count();
|
||||
|
||||
if ($has_return > 0) {
|
||||
return response()->json([
|
||||
'message' => __("You can not update this purchase because it has already been returned.")
|
||||
], 400);
|
||||
}
|
||||
|
||||
if ($purchase->dueAmount) {
|
||||
$party = Party::findOrFail($purchase->party_id);
|
||||
$party->update([
|
||||
'due' => $party->due - $purchase->dueAmount
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($purchase->details as $detail) {
|
||||
Stock::where('id', $detail->stock_id)->decrement('productStock', $detail->quantities);
|
||||
}
|
||||
|
||||
updateBalance($purchase->paidAmount, 'increment');
|
||||
|
||||
sendNotifyToUser($purchase->id, route('business.purchases.index', ['id' => $purchase->id]), __('Purchase has been deleted.'), $purchase->business_id);
|
||||
|
||||
$purchase->delete();
|
||||
|
||||
DB::commit();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
188
app/Http/Controllers/Api/PurchaseReturnController.php
Normal file
188
app/Http/Controllers/Api/PurchaseReturnController.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Events\MultiPaymentProcessed;
|
||||
use App\Models\Party;
|
||||
use App\Models\Stock;
|
||||
use App\Models\Purchase;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\PurchaseReturn;
|
||||
use App\Models\PurchaseDetails;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PurchaseReturnDetail;
|
||||
|
||||
class PurchaseReturnController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data = PurchaseReturn::with('purchase:id,party_id,isPaid,totalAmount,dueAmount,paidAmount,invoiceNumber', 'purchase.party:id,name', 'details')
|
||||
->whereBetween('return_date', [request()->start_date, request()->end_date])
|
||||
->where('business_id', auth()->user()->business_id)
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'purchase_id' => 'required|exists:purchases,id',
|
||||
'return_qty' => 'required|array',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$purchase = Purchase::with('details')
|
||||
->where('business_id', auth()->user()->business_id)
|
||||
->findOrFail($request->purchase_id);
|
||||
|
||||
// Calculate total discount factor
|
||||
$total_discount = $purchase->discountAmount;
|
||||
$total_purchase_amount = $purchase->details->sum(fn($detail) => $detail->productPurchasePrice * $detail->quantities);
|
||||
$discount_per_unit_factor = $total_purchase_amount > 0 ? $total_discount / $total_purchase_amount : 0;
|
||||
|
||||
$purchase_return = PurchaseReturn::create([
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'purchase_id' => $request->purchase_id,
|
||||
'invoice_no' => $purchase->invoiceNumber,
|
||||
'return_date' => now(),
|
||||
]);
|
||||
|
||||
$purchase_return_detail_data = [];
|
||||
$total_return_amount = 0;
|
||||
$total_return_discount = 0;
|
||||
|
||||
// Loop through each purchase detail and process the return
|
||||
foreach ($purchase->details as $key => $detail) {
|
||||
$requested_qty = $request->return_qty[$key];
|
||||
|
||||
if ($requested_qty <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if return quantity exceeds the purchased quantity
|
||||
if ($requested_qty > $detail->quantities) {
|
||||
return response()->json([
|
||||
'message' => "You can't return more than the ordered quantity of {$detail->quantities}.",
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Calculate per-unit discount and return amounts
|
||||
$unit_discount = $detail->productPurchasePrice * $discount_per_unit_factor;
|
||||
$return_discount = $unit_discount * $requested_qty;
|
||||
$return_amount = ($detail->productPurchasePrice - $unit_discount) * $requested_qty;
|
||||
|
||||
$total_return_amount += $return_amount;
|
||||
$total_return_discount += $return_discount;
|
||||
|
||||
// Update stock & purchase details
|
||||
Stock::where('id', $detail->stock_id)->decrement('productStock', $requested_qty);
|
||||
|
||||
$detail->quantities -= $requested_qty;
|
||||
$detail->timestamps = false;
|
||||
$detail->save();
|
||||
|
||||
// Collect return detail data
|
||||
$purchase_return_detail_data[] = [
|
||||
'purchase_detail_id' => $detail->id,
|
||||
'purchase_return_id' => $purchase_return->id,
|
||||
'return_qty' => $requested_qty,
|
||||
'business_id' => auth()->user()->business_id,
|
||||
'return_amount' => $return_amount,
|
||||
];
|
||||
}
|
||||
|
||||
// Insert purchase return details
|
||||
if (!empty($purchase_return_detail_data)) {
|
||||
PurchaseReturnDetail::insert($purchase_return_detail_data);
|
||||
}
|
||||
|
||||
if ($total_return_amount <= 0) {
|
||||
return response()->json("You cannot return an empty product.", 400);
|
||||
}
|
||||
|
||||
// Update party dues (if applicable)
|
||||
$party = Party::find($purchase->party_id);
|
||||
|
||||
if ($party) {
|
||||
$refund_amount = $total_return_amount;
|
||||
|
||||
// If party has due, reduce it first
|
||||
if ($party->due > 0) {
|
||||
if ($party->due >= $refund_amount) {
|
||||
$party->decrement('due', $refund_amount);
|
||||
$refund_amount = 0;
|
||||
} else {
|
||||
$refund_amount -= $party->due;
|
||||
$party->update(['due' => 0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Any remaining amount should be deducted from wallet
|
||||
if ($refund_amount > 0 && $party->wallet > 0) {
|
||||
$deduct = min($party->wallet, $refund_amount);
|
||||
$party->decrement('wallet', $deduct);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate remaining return amount
|
||||
$remaining_return_amount = max(0, $total_return_amount - $purchase->dueAmount);
|
||||
$new_total_amount = max(0, $purchase->totalAmount - $total_return_amount);
|
||||
|
||||
// Update purchase record
|
||||
$purchase->update([
|
||||
'change_amount' => 0,
|
||||
'dueAmount' => max(0, $purchase->dueAmount - $total_return_amount),
|
||||
'paidAmount' => max(0, $purchase->paidAmount - min($purchase->paidAmount, $total_return_amount)),
|
||||
'totalAmount' => $new_total_amount,
|
||||
'discountAmount' => max(0, $purchase->discountAmount - $total_return_discount),
|
||||
'isPaid' => $remaining_return_amount > 0 ? 1 : $purchase->isPaid,
|
||||
]);
|
||||
|
||||
$payments = $request->payments ?? [];
|
||||
$payments = collect($payments)->map(function ($payment) use ($total_return_amount) {
|
||||
$payment['amount'] = $total_return_amount;
|
||||
return $payment;
|
||||
})->toArray();
|
||||
|
||||
event(new MultiPaymentProcessed(
|
||||
$payments,
|
||||
$purchase_return->id,
|
||||
'purchase_return',
|
||||
$total_return_amount ?? 0,
|
||||
));
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $purchase_return,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json(['error' => 'Transaction failed: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$data = PurchaseReturn::with('purchase:id,party_id,isPaid,totalAmount,dueAmount,paidAmount,invoiceNumber', 'purchase.party:id,name', 'details')->findOrFail($id);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
}
|
||||
87
app/Http/Controllers/Api/RackController.php
Normal file
87
app/Http/Controllers/Api/RackController.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Rack;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class RackController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = Rack::with('shelves:id,name')->where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'shelf_id' => 'required|array',
|
||||
'shelf_id.*' => 'exists:shelves,id',
|
||||
'name' => 'required|string|max:255',
|
||||
'status' => 'required|in:0,1',
|
||||
]);
|
||||
|
||||
$rack = Rack::create($request->except('business_id') + [
|
||||
'business_id' => auth()->user()->business_id
|
||||
]);
|
||||
|
||||
$rack->shelves()->sync($request->shelf_id);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $rack,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
$rack = Rack::find($id);
|
||||
if (!$rack) {
|
||||
return response()->json([
|
||||
'message' => __('Rack not found.'),
|
||||
'data' => null,
|
||||
], 404);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'shelf_id' => 'required|array',
|
||||
'shelf_id.*' => 'exists:shelves,id',
|
||||
'name' => 'required|string|max:255',
|
||||
'status' => 'required|in:0,1',
|
||||
]);
|
||||
|
||||
$rack->update($request->except('business_id') + [
|
||||
'business_id' => auth()->user()->business_id
|
||||
]);
|
||||
|
||||
$rack->shelves()->sync($request->shelf_id);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data updated successfully.'),
|
||||
'data' => $rack,
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
$rack = Rack::find($id);
|
||||
|
||||
if (!$rack) {
|
||||
return response()->json([
|
||||
'message' => __('Rack not found.'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
$rack->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
255
app/Http/Controllers/Api/SaleReturnController.php
Normal file
255
app/Http/Controllers/Api/SaleReturnController.php
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Events\MultiPaymentProcessed;
|
||||
use App\Models\Sale;
|
||||
use App\Models\Party;
|
||||
use App\Models\Stock;
|
||||
use App\Models\SaleReturn;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\SaleReturnDetails;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class SaleReturnController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data = SaleReturn::with('sale:id,party_id,isPaid,totalAmount,dueAmount,paidAmount,invoiceNumber', 'sale.party:id,name', 'details')
|
||||
->whereBetween('return_date', [request()->start_date, request()->end_date])
|
||||
->where('business_id', auth()->user()->business_id)
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'sale_id' => 'required|exists:sales,id',
|
||||
'return_qty' => 'required|array',
|
||||
]);
|
||||
|
||||
$business_id = auth()->user()->business_id;
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$sale = Sale::with('details:id,sale_id,product_id,price,discount,lossProfit,quantities,productPurchasePrice,stock_id,expire_date', 'details.product:id,product_type', 'details.product.combo_products')
|
||||
->where('business_id', $business_id)
|
||||
->findOrFail($request->sale_id);
|
||||
|
||||
// Calculate total discount factor with itemwise discount
|
||||
$total_discount = $sale->discountAmount;
|
||||
$total_sale_amount = $sale->details->sum(fn($detail) => $detail->price * $detail->quantities);
|
||||
$discount_per_unit_factor = $total_sale_amount > 0 ? $total_discount / $total_sale_amount : 0;
|
||||
$rounding_amount_per_unit = $sale->details->sum('quantities') > 0 ? $sale->rounding_amount / $sale->details->sum('quantities') : 0;
|
||||
|
||||
$sale_return = SaleReturn::create([
|
||||
'business_id' => $business_id,
|
||||
'sale_id' => $request->sale_id,
|
||||
'invoice_no' => $sale->invoiceNumber,
|
||||
'return_date' => now(),
|
||||
]);
|
||||
|
||||
$sale_return_detail_data = [];
|
||||
$total_return_amount = 0;
|
||||
$total_return_discount = 0;
|
||||
$total_loss_profit_adjustment = 0;
|
||||
|
||||
foreach ($sale->details as $key => $detail) {
|
||||
$requested_qty = $request->return_qty[$key];
|
||||
|
||||
if ($requested_qty <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($requested_qty > $detail->quantities) {
|
||||
return response()->json([
|
||||
'message' => "You can't return more than ordered quantity of {$detail->quantities}.",
|
||||
], 400);
|
||||
}
|
||||
|
||||
$product = $detail->product;
|
||||
|
||||
// Include SaleDetails discount in return calculation
|
||||
$unit_discount = $detail->price * $discount_per_unit_factor;
|
||||
$item_cart_discount = $detail->discount ?? 0;
|
||||
$total_discount_per_unit = $unit_discount + $item_cart_discount;
|
||||
|
||||
$return_discount = $total_discount_per_unit * $requested_qty;
|
||||
$return_amount = ($detail->price - $total_discount_per_unit + $rounding_amount_per_unit) * $requested_qty;
|
||||
|
||||
$total_return_amount += $return_amount;
|
||||
$total_return_discount += $return_discount;
|
||||
|
||||
if ($product && $product->product_type === 'combo') {
|
||||
|
||||
$combo_total_purchase = 0;
|
||||
$combo_total_sale = $detail->price * $requested_qty;
|
||||
|
||||
foreach ($product->combo_products as $comboItem) {
|
||||
$stock = Stock::find($comboItem->stock_id);
|
||||
|
||||
if (!$stock) {
|
||||
return response()->json([
|
||||
'message' => __("Stock not found for combo item '{$comboItem->product->productName}'"),
|
||||
], 400);
|
||||
}
|
||||
|
||||
// increase stock by combo component quantity * returned qty
|
||||
$restore_qty = $comboItem->quantity * $requested_qty;
|
||||
$stock->increment('productStock', $restore_qty);
|
||||
|
||||
$combo_total_purchase += $comboItem->purchase_price * $restore_qty;
|
||||
}
|
||||
|
||||
// loss/profit adjustment for combo
|
||||
$loss_profit_adjustment = ($combo_total_sale - $combo_total_purchase) - $return_discount;
|
||||
$total_loss_profit_adjustment += $loss_profit_adjustment;
|
||||
}
|
||||
|
||||
else {
|
||||
$stock = Stock::where('id', $detail->stock_id)->first();
|
||||
if (!$stock) {
|
||||
return response()->json(['error' => 'Stock not found.'], 404);
|
||||
}
|
||||
|
||||
$stock->increment('productStock', $requested_qty);
|
||||
|
||||
$loss_profit_adjustment = (($detail->price - $stock->productPurchasePrice) * $requested_qty) - $return_discount;
|
||||
$total_loss_profit_adjustment += $loss_profit_adjustment;
|
||||
}
|
||||
|
||||
// Update sale details
|
||||
$detail->quantities -= $requested_qty;
|
||||
$detail->lossProfit -= $loss_profit_adjustment;
|
||||
$detail->timestamps = false;
|
||||
$detail->save();
|
||||
|
||||
$sale_return_detail_data[] = [
|
||||
'business_id' => $business_id,
|
||||
'sale_detail_id' => $detail->id,
|
||||
'sale_return_id' => $sale_return->id,
|
||||
'return_qty' => $requested_qty,
|
||||
'return_amount' => $return_amount,
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($sale_return_detail_data)) {
|
||||
SaleReturnDetails::insert($sale_return_detail_data);
|
||||
}
|
||||
|
||||
if ($total_return_amount <= 0) {
|
||||
return response()->json("You cannot return an empty product.", 400);
|
||||
}
|
||||
|
||||
$remaining_refund = $total_return_amount;
|
||||
|
||||
// Adjust Due
|
||||
$new_due = $sale->dueAmount;
|
||||
if ($remaining_refund > 0) {
|
||||
if ($remaining_refund >= $sale->dueAmount) {
|
||||
$remaining_refund -= $sale->dueAmount;
|
||||
$new_due = 0;
|
||||
} else {
|
||||
$new_due = $sale->dueAmount - $remaining_refund;
|
||||
$remaining_refund = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust Paid (refund part)
|
||||
$new_paid = $sale->paidAmount;
|
||||
if ($remaining_refund > 0) {
|
||||
if ($remaining_refund >= $sale->paidAmount) {
|
||||
$remaining_refund -= $sale->paidAmount;
|
||||
$new_paid = 0;
|
||||
} else {
|
||||
$new_paid = $sale->paidAmount - $remaining_refund;
|
||||
$remaining_refund = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// total sale amount
|
||||
$new_total_amount = max(0, $sale->totalAmount - $total_return_amount);
|
||||
|
||||
$sale->update([
|
||||
'change_amount' => 0,
|
||||
'dueAmount' => $new_due,
|
||||
'paidAmount' => $new_paid,
|
||||
'totalAmount' => $new_total_amount,
|
||||
'actual_total_amount' => $new_total_amount,
|
||||
'discountAmount' => max(0, $sale->discountAmount - $total_return_discount),
|
||||
'lossProfit' => $sale->lossProfit - $total_loss_profit_adjustment,
|
||||
]);
|
||||
|
||||
// Party Refund Logic
|
||||
$party = Party::find($sale->party_id);
|
||||
|
||||
if ($party) {
|
||||
|
||||
// use leftover refund after due/paid adjustments
|
||||
$refund_amount = $remaining_refund;
|
||||
|
||||
// Reduce party due
|
||||
if ($party->due > 0) {
|
||||
if ($party->due >= $refund_amount) {
|
||||
$party->decrement('due', $refund_amount);
|
||||
$refund_amount = 0;
|
||||
} else {
|
||||
$refund_amount -= $party->due;
|
||||
$party->update(['due' => 0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add leftover refund to wallet
|
||||
if ($refund_amount > 0) {
|
||||
$party->increment('wallet', $refund_amount);
|
||||
}
|
||||
}
|
||||
|
||||
$payments = $request->payments ?? [];
|
||||
$payments = collect($payments)->map(function ($payment) use ($total_return_amount) {
|
||||
$payment['amount'] = $total_return_amount;
|
||||
return $payment;
|
||||
})->toArray();
|
||||
|
||||
event(new MultiPaymentProcessed(
|
||||
$payments,
|
||||
$sale_return->id,
|
||||
'sale_return',
|
||||
$total_return_amount ?? 0,
|
||||
));
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $sale_return,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return response()->json(['error' => 'Transaction failed: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$data = SaleReturn::with('sale:id,party_id,isPaid,totalAmount,dueAmount,paidAmount,invoiceNumber', 'sale.party:id,name', 'details')->findOrFail($id);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
}
|
||||
80
app/Http/Controllers/Api/ShelfController.php
Normal file
80
app/Http/Controllers/Api/ShelfController.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Shelf;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ShelfController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = Shelf::where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'status' => 'required|in:0,1',
|
||||
]);
|
||||
|
||||
$shelf = Shelf::create($request->except('business_id') + [
|
||||
'business_id' => auth()->user()->business_id
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $shelf,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
$shelf = Shelf::find($id);
|
||||
|
||||
if (!$shelf) {
|
||||
return response()->json([
|
||||
'message' => __('Shelf not found.'),
|
||||
'data' => null,
|
||||
], 404);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'status' => 'required|in:0,1',
|
||||
]);
|
||||
|
||||
$shelf->update($request->except('business_id') + [
|
||||
'business_id' => auth()->user()->business_id
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data updated successfully.'),
|
||||
'data' => $shelf,
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
$shelf = Shelf::find($id);
|
||||
|
||||
if (!$shelf) {
|
||||
return response()->json([
|
||||
'message' => __('Shelf not found.'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
$shelf->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
238
app/Http/Controllers/Api/StatisticsController.php
Normal file
238
app/Http/Controllers/Api/StatisticsController.php
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Sale;
|
||||
use App\Models\Income;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Product;
|
||||
use App\Models\Category;
|
||||
use App\Models\Purchase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class StatisticsController extends Controller
|
||||
{
|
||||
public function summary()
|
||||
{
|
||||
$business_id = auth()->user()->business_id;
|
||||
$date = request('date') ?? today();
|
||||
|
||||
$sale_profit = Sale::where('business_id', $business_id)->whereDate('created_at', $date)->where('lossProfit', '>', 0)->sum('lossProfit');
|
||||
|
||||
$data = [
|
||||
'sales' => (float)Sale::where('business_id', $business_id)->whereDate('created_at', $date)->sum('totalAmount'),
|
||||
'income' => (float)Income::where('business_id', $business_id)->whereDate('created_at', $date)->sum('amount') + $sale_profit,
|
||||
'expense' => (float)Expense::where('business_id', $business_id)->whereDate('created_at', $date)->sum('amount'),
|
||||
'purchase' => (float)Purchase::where('business_id', $business_id)->whereDate('created_at', $date)->sum('totalAmount'),
|
||||
];
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function dashboard()
|
||||
{
|
||||
$currentDate = Carbon::now();
|
||||
$business_id = auth()->user()->business_id;
|
||||
$duration = request('duration');
|
||||
|
||||
// Set date range, format, and period based on selected duration
|
||||
switch ($duration) {
|
||||
case 'today':
|
||||
$start = $currentDate->copy()->startOfDay();
|
||||
$end = $currentDate->copy()->endOfDay();
|
||||
$format = 'H';
|
||||
$period = $start->hoursUntil($end);
|
||||
break;
|
||||
|
||||
case 'yesterday':
|
||||
$start = $currentDate->copy()->subDay()->startOfDay();
|
||||
$end = $currentDate->copy()->subDay()->endOfDay();
|
||||
$format = 'H';
|
||||
$period = $start->hoursUntil($end);
|
||||
break;
|
||||
|
||||
case 'last_seven_days':
|
||||
$start = $currentDate->copy()->subDays(6)->startOfDay();
|
||||
$end = $currentDate->copy()->endOfDay();
|
||||
$format = 'd';
|
||||
$period = $start->daysUntil($end);
|
||||
break;
|
||||
|
||||
case 'last_thirty_days':
|
||||
$start = $currentDate->copy()->subDays(29)->startOfDay();
|
||||
$end = $currentDate->copy()->endOfDay();
|
||||
$format = 'd';
|
||||
$period = $start->daysUntil($end);
|
||||
break;
|
||||
|
||||
case 'current_month':
|
||||
$start = $currentDate->copy()->startOfMonth();
|
||||
$end = $currentDate->copy()->endOfMonth();
|
||||
$format = 'd';
|
||||
$period = $start->daysUntil($end);
|
||||
break;
|
||||
|
||||
case 'last_month':
|
||||
$start = $currentDate->copy()->subMonthNoOverflow()->startOfMonth();
|
||||
$end = $currentDate->copy()->subMonthNoOverflow()->endOfMonth();
|
||||
$format = 'd';
|
||||
$period = $start->daysUntil($end);
|
||||
break;
|
||||
|
||||
case 'current_year':
|
||||
$start = $currentDate->copy()->startOfYear();
|
||||
$end = $currentDate->copy()->endOfYear();
|
||||
$format = 'M';
|
||||
$period = $start->monthsUntil($end);
|
||||
break;
|
||||
|
||||
case 'custom_date':
|
||||
if (request()->has('from_date') && request()->has('to_date')) {
|
||||
$start = Carbon::parse(request('from_date'))->startOfDay();
|
||||
$end = Carbon::parse(request('to_date'))->endOfDay();
|
||||
$format = 'd';
|
||||
$period = $start->daysUntil($end);
|
||||
} else {
|
||||
return response()->json(['error' => 'From and To dates are required for custom date.'], 400);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return response()->json(['error' => 'Invalid duration'], 400);
|
||||
}
|
||||
|
||||
// SQL date format for grouping
|
||||
$dateFormatSQL = match ($format) {
|
||||
'H' => '%H',
|
||||
'd' => '%Y-%m-%d',
|
||||
'M' => '%Y-%m',
|
||||
default => '%Y-%m-%d',
|
||||
};
|
||||
|
||||
// Sales data fetch and map
|
||||
$sales_data = Sale::selectRaw("DATE_FORMAT(created_at, '$dateFormatSQL') as date, SUM(totalAmount) as amount")
|
||||
->where('business_id', $business_id)
|
||||
->whereBetween('created_at', [$start, $end])
|
||||
->groupBy('date')
|
||||
->orderBy('date')
|
||||
->get()
|
||||
->map(function ($item) {
|
||||
$item->amount = (float) $item->amount;
|
||||
return $item;
|
||||
})
|
||||
->keyBy('date');
|
||||
|
||||
// Purchase data fetch and map
|
||||
$purchase_data = Purchase::selectRaw("DATE_FORMAT(created_at, '$dateFormatSQL') as date, SUM(totalAmount) as amount")
|
||||
->where('business_id', $business_id)
|
||||
->whereBetween('created_at', [$start, $end])
|
||||
->groupBy('date')
|
||||
->orderBy('date')
|
||||
->get()
|
||||
->map(function ($item) {
|
||||
$item->amount = (float) $item->amount;
|
||||
return $item;
|
||||
})
|
||||
->keyBy('date');
|
||||
|
||||
$income_amount = Income::where('business_id', $business_id)
|
||||
->whereBetween('created_at', [$start, $end])
|
||||
->sum('amount');
|
||||
|
||||
$sale_profit = Sale::where('business_id', $business_id)
|
||||
->whereBetween('created_at', [$start, $end])
|
||||
->where('lossProfit', '>', 0)
|
||||
->sum('lossProfit');
|
||||
|
||||
$expense_amount = Expense::where('business_id', $business_id)
|
||||
->whereBetween('created_at', [$start, $end])
|
||||
->sum('amount');
|
||||
|
||||
$productQuery = Product::select('id', 'business_id', 'productName', 'product_type', 'created_at')
|
||||
->with(['stocks:id,business_id,product_id,productStock,productPurchasePrice', 'combo_products.stock'])
|
||||
->where('business_id', $business_id);
|
||||
|
||||
$total_stock_value = 0;
|
||||
|
||||
$products = $productQuery->get()
|
||||
->map(function ($item) {
|
||||
$item->source = 'product';
|
||||
return $item;
|
||||
});
|
||||
|
||||
foreach ($products as $product) {
|
||||
// SINGLE / VARIANT
|
||||
if (in_array($product->product_type, ['single', 'variant'])) {
|
||||
foreach ($product->stocks as $stock) {
|
||||
$total_stock_value += $stock->productStock * $stock->productPurchasePrice;
|
||||
}
|
||||
}
|
||||
|
||||
// COMBO
|
||||
if ($product->product_type === 'combo') {
|
||||
foreach ($product->combo_products as $combo) {
|
||||
$childStock = $combo->stock;
|
||||
if ($childStock) {
|
||||
$total_stock_value += ($childStock->productStock / $combo->quantity) * $combo->purchase_price;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'total_expense' => (float) $expense_amount,
|
||||
'total_income' => (float) $income_amount + $sale_profit,
|
||||
'total_items' => Product::where('business_id', $business_id)->count(),
|
||||
'total_categories' => Category::where('business_id', $business_id)->count(),
|
||||
'stock_value' => $total_stock_value,
|
||||
'total_due' => (float) Sale::where('business_id', $business_id)->whereBetween('saleDate', [$start, $end])->sum('dueAmount'),
|
||||
'total_profit' => (float) Sale::where('business_id', $business_id)->whereBetween('created_at', [$start, $end])->where('lossProfit', '>', 0)->sum('lossProfit'),
|
||||
'total_loss' => (float) Sale::where('business_id', $business_id)->whereBetween('created_at', [$start, $end])->where('lossProfit', '<', 0)->sum('lossProfit'),
|
||||
'sales' => $this->formatData($period, $sales_data, $format),
|
||||
'purchases' => $this->formatData($period, $purchase_data, $format),
|
||||
];
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
private function formatData($period, $datas, $format)
|
||||
{
|
||||
$rows = [];
|
||||
|
||||
foreach ($period as $date) {
|
||||
$key = $date->format($format);
|
||||
|
||||
if ($format === 'M') {
|
||||
// Sum amounts for the month
|
||||
$dateKey = $date->format('Y-m');
|
||||
$amount = $datas->filter(fn($value) => strpos($value->date, $dateKey) === 0)->sum('amount');
|
||||
} elseif ($format === 'd') {
|
||||
// Get amount by full date
|
||||
$fullDateKey = $date->format('Y-m-d');
|
||||
$amount = $datas->get($fullDateKey)?->amount ?? 0;
|
||||
} elseif ($format === 'H') {
|
||||
// Get amount by hour
|
||||
$amount = $datas->get($key)?->amount ?? 0;
|
||||
} else {
|
||||
// Default: treat as full date
|
||||
$fullDateKey = $date->format('Y-m-d');
|
||||
$amount = $datas->get($fullDateKey)?->amount ?? 0;
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'date' => $key,
|
||||
'amount' => $amount,
|
||||
];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
51
app/Http/Controllers/Api/StockController.php
Normal file
51
app/Http/Controllers/Api/StockController.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Stock;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class StockController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'productStock' => 'required|integer',
|
||||
'stock_id' => 'required|exists:stocks,id'
|
||||
]);
|
||||
|
||||
Stock::where('id', $request->stock_id)->increment('productStock', $request->productStock);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
Stock::where('id', $id)->update($request->except('_method'));
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(string $id)
|
||||
{
|
||||
Stock::where('id', $id)->delete();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
110
app/Http/Controllers/Api/TransactionController.php
Normal file
110
app/Http/Controllers/Api/TransactionController.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Transaction;
|
||||
use App\Traits\DateFilterTrait;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TransactionController extends Controller
|
||||
{
|
||||
use DateFilterTrait;
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$businessId = auth()->user()->business_id;
|
||||
|
||||
$transactionsQuery = Transaction::with([
|
||||
'paymentType',
|
||||
'sale.party',
|
||||
'purchase.party',
|
||||
'dueCollect.party'
|
||||
])
|
||||
->where('business_id', $businessId);
|
||||
|
||||
if (request('duration')) {
|
||||
$this->applyDateFilter($transactionsQuery, request('duration'), 'date', request('from_date'), request('to_date'));
|
||||
}
|
||||
|
||||
// Platform filter
|
||||
if (request('platform')) {
|
||||
$transactionsQuery->where('platform', $request->platform);
|
||||
}
|
||||
|
||||
// Party filter
|
||||
if (request('party_id')) {
|
||||
$transactionsQuery->where(function ($q) use ($request) {
|
||||
|
||||
$q->where(function ($saleQ) use ($request) {
|
||||
$saleQ->where('platform', 'sale')
|
||||
->whereHas('sale', fn($s) => $s->where('party_id', $request->party_id)
|
||||
);
|
||||
});
|
||||
|
||||
$q->orWhere(function ($purQ) use ($request) {
|
||||
$purQ->where('platform', 'purchase')
|
||||
->whereHas('purchase', fn($p) => $p->where('party_id', $request->party_id)
|
||||
);
|
||||
});
|
||||
|
||||
$q->orWhere(function ($dueQ) use ($request) {
|
||||
$dueQ->where('platform', 'due_collect')
|
||||
->whereHas('dueCollect', fn($d) => $d->where('party_id', $request->party_id)
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Summary
|
||||
$total_tr_amount = (clone $transactionsQuery)->sum('amount');
|
||||
|
||||
$total_tr_money_in = (clone $transactionsQuery)
|
||||
->where('type', 'credit')
|
||||
->sum('amount');
|
||||
|
||||
$total_tr_money_out = (clone $transactionsQuery)
|
||||
->where('type', 'debit')
|
||||
->sum('amount');
|
||||
|
||||
// No pagination
|
||||
$transactions = $transactionsQuery->latest()->get();
|
||||
|
||||
|
||||
$transactions->transform(function ($transaction) {
|
||||
$transaction->total_amount = match($transaction->platform) {
|
||||
'sale' => $transaction->sale?->totalAmount ?? 0,
|
||||
'purchase' => $transaction->purchase?->totalAmount ?? 0,
|
||||
'due_collect' => $transaction->dueCollect?->totalDue ?? 0,
|
||||
'sale_return' => $transaction->saleReturn?->sale?->totalAmount ?? 0,
|
||||
'purchase_return' => $transaction->purchaseReturn?->purchase?->totalAmount ?? 0,
|
||||
default => 0
|
||||
};
|
||||
|
||||
return $transaction;
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'total_amount' => $total_tr_amount,
|
||||
'money_in' => $total_tr_money_in,
|
||||
'money_out' => $total_tr_money_out,
|
||||
'data' => $transactions,
|
||||
]);
|
||||
}
|
||||
|
||||
public function moneyReceipt($id)
|
||||
{
|
||||
$transaction = Transaction::with([
|
||||
'paymentType',
|
||||
'sale.party',
|
||||
'purchase.party',
|
||||
'dueCollect.party'
|
||||
])->find($id);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $transaction,
|
||||
]);
|
||||
}
|
||||
}
|
||||
73
app/Http/Controllers/Api/UnitController.php
Normal file
73
app/Http/Controllers/Api/UnitController.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Unit;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UnitController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data = Unit::where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'unitName' => 'required|unique:units,unitName,NULL,id,business_id,' . auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
$data = Unit::create($request->all() + [
|
||||
'business_id' => auth()->user()->business_id
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Unit $unit)
|
||||
{
|
||||
$request->validate([
|
||||
'unitName' => [
|
||||
'required',
|
||||
'unique:units,unitName,' . $unit->id . ',id,business_id,' . auth()->user()->business_id,
|
||||
],
|
||||
]);
|
||||
|
||||
$unit = $unit->update($request->all());
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $unit,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Unit $unit)
|
||||
{
|
||||
$unit->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
90
app/Http/Controllers/Api/VariationController.php
Normal file
90
app/Http/Controllers/Api/VariationController.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Variation;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class VariationController extends Controller
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = Variation::where('business_id', auth()->user()->business_id)->latest()->get();
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data fetched successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'values' => 'required|string',
|
||||
'status' => 'required|in:0,1',
|
||||
]);
|
||||
|
||||
$valuesArray = array_map('trim', explode(',', $request->values));
|
||||
|
||||
$data = Variation::create([
|
||||
'name' => $request->name,
|
||||
'status' => $request->status,
|
||||
'values' => $valuesArray,
|
||||
'business_id' => auth()->user()->business_id,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data saved successfully.'),
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
$variation = Variation::find($id);
|
||||
if (!$variation) {
|
||||
return response()->json([
|
||||
'message' => __('Variation not found.'),
|
||||
'data' => null,
|
||||
], 404);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'values' => 'required|string',
|
||||
'status' => 'required|in:0,1',
|
||||
]);
|
||||
|
||||
$valuesArray = array_map('trim', explode(',', $request->values));
|
||||
|
||||
$variation->update([
|
||||
'name' => $request->name,
|
||||
'status' => $request->status,
|
||||
'values' => $valuesArray,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Data updated successfully.'),
|
||||
'data' => $variation,
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
$variation = Variation::find($id);
|
||||
|
||||
if (!$variation) {
|
||||
return response()->json([
|
||||
'message' => __('Variation not found.'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
$variation->delete();
|
||||
return response()->json([
|
||||
'message' => __('Data deleted successfully.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user