added gholak
This commit is contained in:
@@ -204,14 +204,16 @@ class CustomerController extends SuperController
|
||||
$catalogMetalIds = $balanceService->catalogMetalIds();
|
||||
$mainWalletMetalId = $balanceService->resolveMainWalletMetalId();
|
||||
$productCols = implode(',', self::WALLET_PRODUCT_COLUMNS);
|
||||
$ownerType = (int) (Customer::find($customer_id)?->type ?? 0);
|
||||
|
||||
// فقط کیف محصولها (نه کیف اصلی) — هر ردیف = موجودی واقعی یک محصول/فلز
|
||||
// فقط کیف محصول آزاد (نه کیف اصلی، نه قلک) — هر ردیف = یک دارایی
|
||||
$query_table = Wallet::with([
|
||||
'product' => static function ($q) use ($productCols) {
|
||||
$q->withTrashed()->select(explode(',', $productCols));
|
||||
},
|
||||
])
|
||||
->where('owner', $customer_id)
|
||||
->whereNull('fund_id')
|
||||
->whereNotNull('product_id');
|
||||
if ($mainWalletProductIds !== []) {
|
||||
// کیفهای قدیمیِ محصولات متصل به کیف اصلی نمایش داده نمیشوند
|
||||
@@ -240,11 +242,15 @@ class CustomerController extends SuperController
|
||||
->first();
|
||||
|
||||
if ($representative && $mainWallet) {
|
||||
$freeGold = $balanceService->effectiveMainWalletGold((int) $customer_id, $ownerType);
|
||||
$fundGold = $balanceService->fundGoldForMainWalletProducts((int) $customer_id, $ownerType);
|
||||
$mainRow = [
|
||||
'id' => $mainWallet->id,
|
||||
'owner' => (int) $customer_id,
|
||||
'cash' => 0,
|
||||
'gold' => (float) $mainWallet->gold,
|
||||
'gold' => $freeGold + $fundGold,
|
||||
'free_gold' => $freeGold,
|
||||
'fund_gold' => $fundGold,
|
||||
'fund_id' => null,
|
||||
'product_id' => (int) $representative->id,
|
||||
'created_at' => $mainWallet->created_at,
|
||||
@@ -260,7 +266,6 @@ class CustomerController extends SuperController
|
||||
if (isset($query_table['rows'])) {
|
||||
foreach ($query_table['rows'] as &$row) {
|
||||
$balanceService->enrichWalletTableRow($row, $catalogMetalIds, $mainWalletMetalId);
|
||||
// اگر eager-load محصول نیامده بود، از lookup پر کن
|
||||
$productId = (int) (is_array($row) ? ($row['product_id'] ?? 0) : ($row->product_id ?? 0));
|
||||
$hasProduct = is_array($row)
|
||||
? ! empty($row['product'])
|
||||
@@ -278,12 +283,149 @@ class CustomerController extends SuperController
|
||||
$balanceService->enrichWalletTableRow($row, $catalogMetalIds, $mainWalletMetalId);
|
||||
}
|
||||
}
|
||||
|
||||
// جمع دارایی = آزاد + قلکهای همان محصول (برای ردیف اصلی قبلاً ست شده)
|
||||
$isMain = is_array($row)
|
||||
? ! empty($row['is_main_wallet'])
|
||||
: ! empty($row->is_main_wallet);
|
||||
if ($productId > 0 && ! $isMain) {
|
||||
$freeGold = $balanceService->freeProductGold((int) $customer_id, $productId, $ownerType);
|
||||
$fundGold = $balanceService->fundGoldForProduct((int) $customer_id, $productId, $ownerType);
|
||||
if (is_array($row)) {
|
||||
$row['free_gold'] = $freeGold;
|
||||
$row['fund_gold'] = $fundGold;
|
||||
$row['gold'] = $freeGold + $fundGold;
|
||||
} else {
|
||||
$row->free_gold = $freeGold;
|
||||
$row->fund_gold = $fundGold;
|
||||
$row->gold = $freeGold + $fundGold;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($row);
|
||||
}
|
||||
|
||||
return response()->json($query_table);
|
||||
}
|
||||
|
||||
/** جدول قلکهای واقعی مشتری (مدل Fund) */
|
||||
public function piggyFunds(Request $request, $customer_id)
|
||||
{
|
||||
$query_table = \App\Models\Fund::with(['wallet', 'product'])
|
||||
->where('customer_id', $customer_id);
|
||||
|
||||
TableEngine::create($query_table, $request)
|
||||
->initSearch(['title'])
|
||||
->select(['*'])
|
||||
->export($query_table);
|
||||
|
||||
if (isset($query_table['rows'])) {
|
||||
foreach ($query_table['rows'] as &$row) {
|
||||
$wallets = is_array($row)
|
||||
? collect($row['wallet'] ?? [])
|
||||
: collect($row->wallet ?? []);
|
||||
$gold = (float) $wallets->sum('gold');
|
||||
if (is_array($row)) {
|
||||
$row['gold'] = $gold;
|
||||
$row['product_title'] = $row['product']['title'] ?? ($row['product']->title ?? null);
|
||||
} else {
|
||||
$row->gold = $gold;
|
||||
$row->product_title = $row->product?->title;
|
||||
}
|
||||
}
|
||||
unset($row);
|
||||
}
|
||||
|
||||
return response()->json($query_table);
|
||||
}
|
||||
|
||||
/** قلکهای یک دارایی (محصول) */
|
||||
public function assetPiggyFunds(Request $request, $customer_id, $product_id)
|
||||
{
|
||||
$request->merge(['product_id' => (int) $product_id]);
|
||||
$query_table = \App\Models\Fund::with(['wallet', 'product'])
|
||||
->where('customer_id', $customer_id)
|
||||
->where('product_id', (int) $product_id);
|
||||
|
||||
TableEngine::create($query_table, $request)
|
||||
->initSearch(['title'])
|
||||
->select(['*'])
|
||||
->export($query_table);
|
||||
|
||||
if (isset($query_table['rows'])) {
|
||||
foreach ($query_table['rows'] as &$row) {
|
||||
$wallets = is_array($row)
|
||||
? collect($row['wallet'] ?? [])
|
||||
: collect($row->wallet ?? []);
|
||||
$gold = (float) $wallets->sum('gold');
|
||||
if (is_array($row)) {
|
||||
$row['gold'] = $gold;
|
||||
} else {
|
||||
$row->gold = $gold;
|
||||
}
|
||||
}
|
||||
unset($row);
|
||||
}
|
||||
|
||||
return response()->json($query_table);
|
||||
}
|
||||
|
||||
/** تراکنشهای مربوط به یک دارایی (کیف آزاد + قلکهای همان محصول) */
|
||||
public function assetTransactions(Request $request, $customer_id, $product_id)
|
||||
{
|
||||
$config = app(ConfigService::class);
|
||||
$productId = (int) $product_id;
|
||||
$walletIds = Wallet::query()
|
||||
->where('owner', $customer_id)
|
||||
->where(function ($q) use ($config, $productId) {
|
||||
if ($config->isMainWalletProduct($productId)) {
|
||||
$q->where(function ($inner) {
|
||||
$inner->whereNull('fund_id')->whereNull('product_id');
|
||||
})->orWhere(function ($inner) use ($productId) {
|
||||
$inner->whereNotNull('fund_id')
|
||||
->where(function ($f) use ($productId) {
|
||||
$f->where('product_id', $productId)->orWhereNull('product_id');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$q->where('product_id', $productId);
|
||||
}
|
||||
})
|
||||
->pluck('id');
|
||||
|
||||
$records = \Rooyesh\Wallet\models\Transaction::whereIn('wallet', $walletIds);
|
||||
TableEngine::create($records, $request->all())
|
||||
->initSearch([])
|
||||
->select([
|
||||
'*',
|
||||
\Rooyesh\Wallet\models\Transaction::$SELECT_CAJ,
|
||||
\Rooyesh\Wallet\models\Transaction::$SELECT_TYPE_STR,
|
||||
])->export($records);
|
||||
|
||||
return response()->json($records);
|
||||
}
|
||||
|
||||
/** تراکنشهای یک قلک */
|
||||
public function piggyFundTransactions(Request $request, $customer_id, $fund_id)
|
||||
{
|
||||
$fund = \App\Models\Fund::where('customer_id', $customer_id)->findOrFail($fund_id);
|
||||
$walletIds = Wallet::query()
|
||||
->where('owner', $customer_id)
|
||||
->where('fund_id', $fund->id)
|
||||
->pluck('id');
|
||||
|
||||
$records = \Rooyesh\Wallet\models\Transaction::whereIn('wallet', $walletIds);
|
||||
TableEngine::create($records, $request->all())
|
||||
->initSearch([])
|
||||
->select([
|
||||
'*',
|
||||
\Rooyesh\Wallet\models\Transaction::$SELECT_CAJ,
|
||||
\Rooyesh\Wallet\models\Transaction::$SELECT_TYPE_STR,
|
||||
])->export($records);
|
||||
|
||||
return response()->json($records);
|
||||
}
|
||||
|
||||
public function archive($id){
|
||||
$query = Customer::findOrFail($id);
|
||||
$query->update([
|
||||
|
||||
@@ -2,96 +2,243 @@
|
||||
|
||||
namespace App\Http\Controllers\Customer;
|
||||
|
||||
use App\DB\Te\TableEngine;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\SuperController;
|
||||
use App\Models\Fund;
|
||||
use App\Models\Product;
|
||||
use App\Services\ConfigService;
|
||||
use App\Services\WalletBalanceService;
|
||||
use Illuminate\Http\Request;
|
||||
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
||||
use Rooyesh\Wallet\interfaces\WalletInterface;
|
||||
use Rooyesh\Wallet\models\Transaction;
|
||||
use Rooyesh\Wallet\models\Wallet;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
|
||||
/**
|
||||
* قلک per-product — انتقال فقط TransactionInterface؛ بدون سند حسابداری.
|
||||
*/
|
||||
class FundController extends SuperController
|
||||
{
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$model = Fund::class;
|
||||
$this->with=['wallet'];
|
||||
$this->with = ['wallet', 'product'];
|
||||
$select = ['*'];
|
||||
$search=['name'];
|
||||
$request->request->add(['customer_id'=>auth('customer')->id()]);
|
||||
$query = Fund::with($this->with)->where('customer_id',auth('customer')->id());
|
||||
parent::__construct($model, $this->with, $select, null,$query,$search);
|
||||
$search = ['title'];
|
||||
$request->request->add(['customer_id' => auth('customer')->id()]);
|
||||
$query = Fund::with($this->with)->where('customer_id', auth('customer')->id());
|
||||
parent::__construct($model, $this->with, $select, null, $query, $search);
|
||||
}
|
||||
public function deposit(Request $request){
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->assertProductFundsEnabled();
|
||||
|
||||
$data = $request->all();
|
||||
$wallet_interface = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type,null);
|
||||
$wallet = $wallet_interface->wallet;
|
||||
if($wallet->gold < $data['value']){
|
||||
$productId = (int) ($data['product_id'] ?? 0);
|
||||
if ($productId <= 0) {
|
||||
throw new UnprocessableEntityHttpException('انتخاب محصول برای قلک الزامی است');
|
||||
}
|
||||
|
||||
$product = Product::query()->find($productId);
|
||||
if (! $product) {
|
||||
throw new UnprocessableEntityHttpException('محصول یافت نشد');
|
||||
}
|
||||
|
||||
$title = trim((string) ($data['title'] ?? ''));
|
||||
if ($title === '') {
|
||||
throw new UnprocessableEntityHttpException('عنوان قلک الزامی است');
|
||||
}
|
||||
|
||||
$customerId = (int) auth('customer')->id();
|
||||
$fund = Fund::create([
|
||||
'title' => $title,
|
||||
'file_id' => $data['file_id'] ?? null,
|
||||
'customer_id' => $customerId,
|
||||
'product_id' => $productId,
|
||||
]);
|
||||
|
||||
// کیف قلک را از همان ابتدا با product_id بساز (بدون سند حسابداری)
|
||||
new WalletInterface(
|
||||
$customerId,
|
||||
auth('customer')->user()->type,
|
||||
$fund->id,
|
||||
$productId
|
||||
);
|
||||
|
||||
return response()->json($fund->load(['wallet', 'product']));
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->assertProductFundsEnabled();
|
||||
|
||||
$fund = $this->ownedFund((int) $id);
|
||||
$title = trim((string) ($request->input('title') ?? $fund->title));
|
||||
if ($title === '') {
|
||||
throw new UnprocessableEntityHttpException('عنوان قلک الزامی است');
|
||||
}
|
||||
|
||||
// product_id بعد از ساخت عوض نمیشود
|
||||
$fund->update([
|
||||
'title' => $title,
|
||||
'file_id' => $request->input('file_id', $fund->file_id),
|
||||
]);
|
||||
|
||||
return response()->json($fund->fresh(['wallet', 'product']));
|
||||
}
|
||||
|
||||
public function deposit(Request $request)
|
||||
{
|
||||
$this->assertProductFundsEnabled();
|
||||
|
||||
$data = $request->all();
|
||||
$fund = $this->ownedFund((int) ($data['fund_id'] ?? 0));
|
||||
$value = (float) ($data['value'] ?? 0);
|
||||
if ($value <= 0) {
|
||||
throw new UnprocessableEntityHttpException('مقدار نامعتبر است');
|
||||
}
|
||||
|
||||
$customer = auth('customer')->user();
|
||||
$customerId = (int) $customer->id;
|
||||
$ownerType = (int) $customer->type;
|
||||
$productId = (int) ($fund->product_id ?? 0);
|
||||
|
||||
$balanceService = app(WalletBalanceService::class);
|
||||
$free = $productId > 0
|
||||
? $balanceService->freeProductGold($customerId, $productId, $ownerType)
|
||||
: $balanceService->effectiveMainWalletGold($customerId, $ownerType);
|
||||
|
||||
if ($free < $value) {
|
||||
throw new UnprocessableEntityHttpException('موجودی کمتر از مقدار وارد شده است');
|
||||
}
|
||||
|
||||
$transaction = new TransactionInterface($wallet_interface, $_inp = [
|
||||
"gold" => $data['value'],
|
||||
"fund_id"=>$data["fund_id"],
|
||||
'expire_at'=>now(),
|
||||
'active'=>true
|
||||
[$sourceFundId, $sourceProductId] = $this->sourceWalletKeys($productId);
|
||||
$source = new WalletInterface($customerId, $ownerType, $sourceFundId, $sourceProductId);
|
||||
$dest = new WalletInterface($customerId, $ownerType, $fund->id, $productId > 0 ? $productId : null);
|
||||
|
||||
// فقط جابهجایی داخلی — بدون Accounting*
|
||||
new TransactionInterface($source, [
|
||||
'gold' => $value,
|
||||
'fund_id' => $fund->id,
|
||||
'product_id' => $productId > 0 ? $productId : null,
|
||||
'expire_at' => now(),
|
||||
'active' => true,
|
||||
], 6, false);
|
||||
$wallet_interface_fund = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type,$data["fund_id"]);
|
||||
$transaction = new TransactionInterface($wallet_interface_fund, $_inp = [
|
||||
"gold" => $data['value'],
|
||||
"fund_id"=>$data["fund_id"],
|
||||
'expire_at'=>now(),
|
||||
'active'=>true
|
||||
|
||||
new TransactionInterface($dest, [
|
||||
'gold' => $value,
|
||||
'fund_id' => $fund->id,
|
||||
'product_id' => $productId > 0 ? $productId : null,
|
||||
'expire_at' => now(),
|
||||
'active' => true,
|
||||
], 5, true);
|
||||
|
||||
return response()->json(true);
|
||||
}
|
||||
public function withdraw(Request $request){
|
||||
$data = $request->all();
|
||||
$wallet_interface_fund = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type,$data["fund_id"]);
|
||||
$wallet = $wallet_interface_fund->wallet;
|
||||
if($wallet->gold < $data['value']){
|
||||
throw new UnprocessableEntityHttpException('موجودی کمتر از مقدار وارد شده است');
|
||||
}
|
||||
$transaction = new TransactionInterface($wallet_interface_fund, $_inp = [
|
||||
"gold" => $data['value'],
|
||||
"fund_id"=>$data["fund_id"],
|
||||
'expire_at'=>now(),
|
||||
'active'=>true
|
||||
], 6, false);
|
||||
$wallet_interface = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type);
|
||||
$transaction = new TransactionInterface($wallet_interface, $_inp = [
|
||||
"gold" => $data['value'],
|
||||
"fund_id"=>$data["fund_id"],
|
||||
'expire_at'=>now(),
|
||||
'active'=>true
|
||||
], 5, true);
|
||||
return response()->json(true);
|
||||
}
|
||||
public function show($id){
|
||||
$query = Fund::with($this->with)->findOrFail($id);
|
||||
return response()->json($query);
|
||||
}
|
||||
|
||||
public function destroy($id){
|
||||
$wallet_interface_fund = Wallet::where('owner',auth('customer')->id())->where('fund_id',$id)->first();
|
||||
if (!is_null($wallet_interface_fund)){
|
||||
if ($wallet_interface_fund->gold > 0){
|
||||
throw new UnprocessableEntityHttpException('به علت داشتن موجودی امکان حذف صندوق وجود ندارد');
|
||||
}
|
||||
public function withdraw(Request $request)
|
||||
{
|
||||
$this->assertProductFundsEnabled();
|
||||
|
||||
$data = $request->all();
|
||||
$fund = $this->ownedFund((int) ($data['fund_id'] ?? 0));
|
||||
$value = (float) ($data['value'] ?? 0);
|
||||
if ($value <= 0) {
|
||||
throw new UnprocessableEntityHttpException('مقدار نامعتبر است');
|
||||
}
|
||||
Fund::destroy($id);
|
||||
|
||||
$customer = auth('customer')->user();
|
||||
$customerId = (int) $customer->id;
|
||||
$ownerType = (int) $customer->type;
|
||||
$productId = (int) ($fund->product_id ?? 0);
|
||||
|
||||
$source = new WalletInterface($customerId, $ownerType, $fund->id, $productId > 0 ? $productId : null);
|
||||
if ((float) $source->wallet->gold < $value) {
|
||||
throw new UnprocessableEntityHttpException('موجودی کمتر از مقدار وارد شده است');
|
||||
}
|
||||
|
||||
[$destFundId, $destProductId] = $this->sourceWalletKeys($productId);
|
||||
$dest = new WalletInterface($customerId, $ownerType, $destFundId, $destProductId);
|
||||
|
||||
new TransactionInterface($source, [
|
||||
'gold' => $value,
|
||||
'fund_id' => $fund->id,
|
||||
'product_id' => $productId > 0 ? $productId : null,
|
||||
'expire_at' => now(),
|
||||
'active' => true,
|
||||
], 6, false);
|
||||
|
||||
new TransactionInterface($dest, [
|
||||
'gold' => $value,
|
||||
'fund_id' => $fund->id,
|
||||
'product_id' => $productId > 0 ? $productId : null,
|
||||
'expire_at' => now(),
|
||||
'active' => true,
|
||||
], 5, true);
|
||||
|
||||
return response()->json(true);
|
||||
}
|
||||
public function transactionTable(Request $request){
|
||||
$records = Transaction::where('wallet',$request->wallet_id);
|
||||
TableEngine::create($records, $request->all())
|
||||
->initSearch([])
|
||||
->select([
|
||||
'*',Transaction::$SELECT_CAJ,Transaction::$SELECT_TYPE_STR
|
||||
])->export($records);
|
||||
return response()->json($records);
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$fund = $this->ownedFund((int) $id);
|
||||
|
||||
return response()->json($fund->load(['wallet', 'product']));
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->assertProductFundsEnabled();
|
||||
|
||||
$fund = $this->ownedFund((int) $id);
|
||||
$wallet = Wallet::where('owner', auth('customer')->id())
|
||||
->where('fund_id', $fund->id)
|
||||
->first();
|
||||
|
||||
if ($wallet && (float) $wallet->gold > 0) {
|
||||
throw new UnprocessableEntityHttpException('به علت داشتن موجودی امکان حذف قلک وجود ندارد');
|
||||
}
|
||||
|
||||
Fund::destroy($fund->id);
|
||||
|
||||
return response()->json(true);
|
||||
}
|
||||
|
||||
private function assertProductFundsEnabled(): void
|
||||
{
|
||||
if (! app(ConfigService::class)->isProductFundsEnabled()) {
|
||||
throw new UnprocessableEntityHttpException('قابلیت قلک محصول غیرفعال است');
|
||||
}
|
||||
}
|
||||
|
||||
private function ownedFund(int $id): Fund
|
||||
{
|
||||
if ($id <= 0) {
|
||||
throw new UnprocessableEntityHttpException('قلک نامعتبر است');
|
||||
}
|
||||
|
||||
$fund = Fund::with(['wallet', 'product'])
|
||||
->where('customer_id', auth('customer')->id())
|
||||
->find($id);
|
||||
|
||||
if (! $fund) {
|
||||
throw new UnprocessableEntityHttpException('قلک یافت نشد');
|
||||
}
|
||||
|
||||
return $fund;
|
||||
}
|
||||
|
||||
/** @return array{0: null, 1: int|null} */
|
||||
private function sourceWalletKeys(int $productId): array
|
||||
{
|
||||
if ($productId <= 0) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
if (app(ConfigService::class)->isMainWalletProduct($productId)) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
return [null, $productId];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,16 @@ class WalletController extends Controller
|
||||
},
|
||||
'wallet.product',
|
||||
'product',
|
||||
])->whereIn('wallet', $walletIds)->whereNull('fund_id');
|
||||
])->whereIn('wallet', $walletIds);
|
||||
|
||||
// جدول عمومی تراکنشها: جابهجایی قلک را قاطی نکند؛ برای کیف قلک همهٔ ردیفها بیاید
|
||||
$includesFundWallet = Wallet::query()
|
||||
->whereIn('id', $walletIds)
|
||||
->whereNotNull('fund_id')
|
||||
->exists();
|
||||
if (! $includesFundWallet) {
|
||||
$records->whereNull('fund_id');
|
||||
}
|
||||
|
||||
TableEngine::create($records, $request->all())
|
||||
->initSearch(['customer.name'])
|
||||
|
||||
@@ -323,9 +323,9 @@ class PargarInboundController extends Controller
|
||||
$walletInterface = new WalletInterface($customer->id, $ownerType);
|
||||
$wallet = $walletInterface->wallet;
|
||||
|
||||
// GoldInv پرگار فقط با طلای کیف اصلی مقایسه میشود — محصولات جدا سهمی ندارند
|
||||
// GoldInv پرگار با جمع آزاد+قلک محصولات کیف اصلی مقایسه میشود (انتقال قلک سند نمیزند)
|
||||
$appGold = app(WalletBalanceService::class)
|
||||
->effectiveMainWalletGold((int) $customer->id, $ownerType);
|
||||
->accountingVisibleMainWalletGold((int) $customer->id, $ownerType);
|
||||
$appCash = (float) ($wallet->cash ?? 0);
|
||||
$goldDiff = $goldInv - $appGold;
|
||||
$rialDiff = $rialInv - $appCash;
|
||||
|
||||
Reference in New Issue
Block a user