fixed taraz for all metals
This commit is contained in:
@@ -10,6 +10,7 @@ use App\Models\Price;
|
||||
use App\Models\Product;
|
||||
use App\Models\TransactionsAdmin;
|
||||
use App\Models\Withdraw;
|
||||
use App\Services\BalanceReportService;
|
||||
use App\Services\ConfigService;
|
||||
use Carbon\Carbon;
|
||||
use Hekmatinasser\Verta\Verta;
|
||||
@@ -149,7 +150,7 @@ class DashboardController extends Controller
|
||||
return $cnt;
|
||||
}
|
||||
|
||||
public function getBalance(Request $request)
|
||||
public function getBalance(Request $request, BalanceReportService $balanceReportService)
|
||||
{
|
||||
$startDate = Carbon::parse($request->startDate)->startOfDay();
|
||||
$endDate = Carbon::parse($request->endDate)->endOfDay();
|
||||
@@ -158,6 +159,12 @@ class DashboardController extends Controller
|
||||
$response["kalhor"]=[];
|
||||
|
||||
$excludeCustomerId = (int) app(ConfigService::class)->getInt('balance_report_exclude_customer_id', 4);
|
||||
$catalogMetalIds = $balanceReportService->catalogMetalIds();
|
||||
if ($catalogMetalIds === []) {
|
||||
$catalogMetalIds = [1];
|
||||
}
|
||||
$limanMetalBuckets = $balanceReportService->initMetalBalanceBuckets($catalogMetalIds);
|
||||
$kalhorMetalBuckets = $balanceReportService->initMetalBalanceBuckets($catalogMetalIds);
|
||||
|
||||
$response["pay_at_update"] = Order::whereNull('pay_at')->update(['pay_at' => DB::raw('created_at')]);
|
||||
//liman
|
||||
@@ -171,6 +178,8 @@ class DashboardController extends Controller
|
||||
$response["liman"]["balance_abs"]["gold"] = 0;
|
||||
$response["liman"]["balance_abs"]["cash"] = 0;
|
||||
foreach ($response["liman"]["orders"] as $OrderGoldBalanceRow) {
|
||||
$balanceReportService->applyOrderToBuckets($limanMetalBuckets, $OrderGoldBalanceRow);
|
||||
|
||||
if ($OrderGoldBalanceRow->direction == 1){
|
||||
$response["liman"]["balance"]["gold"] += $OrderGoldBalanceRow->amount;
|
||||
$response["liman"]["balance"]["cash"] -= $OrderGoldBalanceRow->total_price;
|
||||
@@ -212,10 +221,22 @@ class DashboardController extends Controller
|
||||
$response["kalhor"]["balance"]['gold'] += $data['Count'];
|
||||
$response["kalhor"]["balance"]['cash'] -= $data['TotalPrice'];
|
||||
$direction =2;
|
||||
$balanceReportService->applyKalhorSlice(
|
||||
$kalhorMetalBuckets,
|
||||
(float) $data['Count'],
|
||||
(float) $data['TotalPrice'],
|
||||
2
|
||||
);
|
||||
} elseif ($data['DealTypeStr'] == "فروش" or $data['DealTypeStr'] == "فروش _ دستی") {
|
||||
$response["kalhor"]["balance"]['gold'] -= $data['Count'];
|
||||
$response["kalhor"]["balance"]['cash'] += $data['TotalPrice'];
|
||||
$direction =1;
|
||||
$balanceReportService->applyKalhorSlice(
|
||||
$kalhorMetalBuckets,
|
||||
(float) $data['Count'],
|
||||
(float) $data['TotalPrice'],
|
||||
1
|
||||
);
|
||||
}
|
||||
$response["kalhor"]["balance_abs"]['gold'] += $data['Count'];
|
||||
$response["kalhor"]["balance_abs"]['cash'] += $data['TotalPrice'];
|
||||
@@ -244,6 +265,12 @@ class DashboardController extends Controller
|
||||
//main
|
||||
$response['cashBalance']= -$response["liman"]["balance"]["cash"]+$response["kalhor"]["balance"]["cash"];
|
||||
$response['goldBalance']= -$response["liman"]["balance"]["gold"]+$response["kalhor"]["balance"]["gold"];
|
||||
|
||||
$metalBalances = $balanceReportService->buildMetalBalancesResponse($limanMetalBuckets, $kalhorMetalBuckets);
|
||||
$response['metalBalances'] = $metalBalances['items'];
|
||||
if (!empty($metalBalances['items'])) {
|
||||
$response['goldBalance'] = $metalBalances['totalGoldBalance'];
|
||||
}
|
||||
//mazaneh
|
||||
$changeMesghal = config('change_mesghal');
|
||||
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Models\Product;
|
||||
|
||||
class BalanceReportService
|
||||
{
|
||||
public const METAL_LABELS = [
|
||||
1 => 'طلا',
|
||||
2 => 'نقره',
|
||||
3 => 'مس',
|
||||
];
|
||||
|
||||
/** @return list<int> */
|
||||
public function catalogMetalIds(): array
|
||||
{
|
||||
return Product::query()
|
||||
->where('active', 1)
|
||||
->whereNotNull('metal_id')
|
||||
->where('metal_id', '>', 0)
|
||||
->distinct()
|
||||
->orderBy('metal_id')
|
||||
->pluck('metal_id')
|
||||
->map(static fn ($id) => (int) $id)
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
public function metalLabel(int $metalId): string
|
||||
{
|
||||
return self::METAL_LABELS[$metalId] ?? "فلز {$metalId}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{gold: float, cash: float}>
|
||||
*/
|
||||
public function resolveOrderMetalSlices(Order $order): array
|
||||
{
|
||||
$order->loadMissing(['transactions.product']);
|
||||
|
||||
$slices = [];
|
||||
$txWithProduct = $order->transactions->filter(
|
||||
static fn ($tx) => $tx->product_id && abs((float) ($tx->gold ?? 0)) > 0
|
||||
);
|
||||
|
||||
if ($txWithProduct->isNotEmpty()) {
|
||||
foreach ($txWithProduct as $tx) {
|
||||
$metalId = (int) ($tx->product?->metal_id ?? 1);
|
||||
if ($metalId <= 0) {
|
||||
$metalId = 1;
|
||||
}
|
||||
$gold = abs((float) ($tx->gold ?? 0));
|
||||
if ($gold <= 0) {
|
||||
continue;
|
||||
}
|
||||
$slices[$metalId] = ($slices[$metalId] ?? 0.0) + $gold;
|
||||
}
|
||||
|
||||
$totalGold = array_sum($slices);
|
||||
$result = [];
|
||||
foreach ($slices as $metalId => $gold) {
|
||||
$cashShare = $totalGold > 0
|
||||
? (float) $order->total_price * ($gold / $totalGold)
|
||||
: 0.0;
|
||||
$result[(int) $metalId] = ['gold' => $gold, 'cash' => $cashShare];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$amount = (float) ($order->amount ?? 0);
|
||||
$totalPrice = (float) ($order->total_price ?? 0);
|
||||
if ($amount <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$metalId = 1;
|
||||
$tx = $order->transactions->first(static fn ($t) => $t->product_id);
|
||||
if ($tx?->product) {
|
||||
$metalId = max(1, (int) ($tx->product->metal_id ?? 1));
|
||||
}
|
||||
|
||||
return [$metalId => ['gold' => $amount, 'cash' => $totalPrice]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<int> $metalIds
|
||||
* @return array<int, array{gold: float, cash: float, gold_abs: float, cash_abs: float}>
|
||||
*/
|
||||
public function initMetalBalanceBuckets(array $metalIds): array
|
||||
{
|
||||
$buckets = [];
|
||||
foreach ($metalIds as $metalId) {
|
||||
$buckets[(int) $metalId] = [
|
||||
'gold' => 0.0,
|
||||
'cash' => 0.0,
|
||||
'gold_abs' => 0.0,
|
||||
'cash_abs' => 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
return $buckets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{gold: float, cash: float, gold_abs: float, cash_abs: float}> $buckets
|
||||
*/
|
||||
public function applyOrderToBuckets(array &$buckets, Order $order): void
|
||||
{
|
||||
$slices = $this->resolveOrderMetalSlices($order);
|
||||
foreach ($slices as $metalId => $slice) {
|
||||
$metalId = (int) $metalId;
|
||||
if (! isset($buckets[$metalId])) {
|
||||
$buckets[$metalId] = [
|
||||
'gold' => 0.0,
|
||||
'cash' => 0.0,
|
||||
'gold_abs' => 0.0,
|
||||
'cash_abs' => 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
$gold = (float) $slice['gold'];
|
||||
$cash = (float) $slice['cash'];
|
||||
|
||||
if ((int) $order->direction === 1) {
|
||||
$buckets[$metalId]['gold'] += $gold;
|
||||
$buckets[$metalId]['cash'] -= $cash;
|
||||
} elseif ((int) $order->direction === 2) {
|
||||
$buckets[$metalId]['gold'] -= $gold;
|
||||
$buckets[$metalId]['cash'] += $cash;
|
||||
}
|
||||
|
||||
$buckets[$metalId]['gold_abs'] += $gold;
|
||||
$buckets[$metalId]['cash_abs'] += $cash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{gold: float, cash: float, gold_abs: float, cash_abs: float}> $limanBuckets
|
||||
* @param array<int, array{gold: float, cash: float, gold_abs: float, cash_abs: float}> $kalhorBuckets
|
||||
*/
|
||||
public function buildMetalBalancesResponse(array $limanBuckets, array $kalhorBuckets): array
|
||||
{
|
||||
$metalIds = array_values(array_unique(array_merge(
|
||||
array_keys($limanBuckets),
|
||||
array_keys($kalhorBuckets)
|
||||
)));
|
||||
sort($metalIds);
|
||||
|
||||
$items = [];
|
||||
$totalGoldBalance = 0.0;
|
||||
|
||||
foreach ($metalIds as $metalId) {
|
||||
$metalId = (int) $metalId;
|
||||
$liman = $limanBuckets[$metalId] ?? [
|
||||
'gold' => 0.0,
|
||||
'cash' => 0.0,
|
||||
'gold_abs' => 0.0,
|
||||
'cash_abs' => 0.0,
|
||||
];
|
||||
$kalhor = $kalhorBuckets[$metalId] ?? [
|
||||
'gold' => 0.0,
|
||||
'cash' => 0.0,
|
||||
'gold_abs' => 0.0,
|
||||
'cash_abs' => 0.0,
|
||||
];
|
||||
|
||||
$goldBalance = -$liman['gold'] + $kalhor['gold'];
|
||||
$cashBalance = -$liman['cash'] + $kalhor['cash'];
|
||||
$goldBalanceOfLiman = -$liman['gold'];
|
||||
$goldBalanceOfKalhor = $kalhor['gold'];
|
||||
|
||||
$items[] = [
|
||||
'metal_id' => $metalId,
|
||||
'label' => $this->metalLabel($metalId),
|
||||
'balance' => round($goldBalance, 4),
|
||||
'goldBalance' => round($goldBalance, 4),
|
||||
'cashBalance' => round($cashBalance, 2),
|
||||
'goldBalanceOfLiman' => round($goldBalanceOfLiman, 4),
|
||||
'goldBalanceOfKalhor' => round($goldBalanceOfKalhor, 4),
|
||||
];
|
||||
|
||||
$totalGoldBalance += $goldBalance;
|
||||
}
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'totalGoldBalance' => round($totalGoldBalance, 4),
|
||||
];
|
||||
}
|
||||
|
||||
/** @param array<int, array{gold: float, cash: float, gold_abs: float, cash_abs: float}> $buckets */
|
||||
public function applyKalhorSlice(
|
||||
array &$buckets,
|
||||
float $gold,
|
||||
float $cash,
|
||||
int $direction,
|
||||
int $metalId = 1
|
||||
): void {
|
||||
if (! isset($buckets[$metalId])) {
|
||||
$buckets[$metalId] = [
|
||||
'gold' => 0.0,
|
||||
'cash' => 0.0,
|
||||
'gold_abs' => 0.0,
|
||||
'cash_abs' => 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
if ($direction === 2) {
|
||||
$buckets[$metalId]['gold'] += $gold;
|
||||
$buckets[$metalId]['cash'] -= $cash;
|
||||
} elseif ($direction === 1) {
|
||||
$buckets[$metalId]['gold'] -= $gold;
|
||||
$buckets[$metalId]['cash'] += $cash;
|
||||
}
|
||||
|
||||
$buckets[$metalId]['gold_abs'] += $gold;
|
||||
$buckets[$metalId]['cash_abs'] += $cash;
|
||||
}
|
||||
}
|
||||
@@ -97,27 +97,22 @@ class CategoryService
|
||||
{
|
||||
$names = $this->collectTargetCategoryNames();
|
||||
$created = 0;
|
||||
$order = 0;
|
||||
$maxOrder = (int) Category::max('sort_order');
|
||||
|
||||
foreach ($names as $name) {
|
||||
$order++;
|
||||
$existing = Category::where('name', $name)->first();
|
||||
|
||||
if ($existing) {
|
||||
if ((int) $existing->sort_order !== $order) {
|
||||
$existing->sort_order = $order;
|
||||
$existing->save();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$maxOrder++;
|
||||
Category::create([
|
||||
'name' => $name,
|
||||
'active' => 1,
|
||||
'show_in_filter' => 1,
|
||||
'show_in_select' => 1,
|
||||
'sort_order' => $order,
|
||||
'sort_order' => $maxOrder,
|
||||
]);
|
||||
$created++;
|
||||
}
|
||||
|
||||
@@ -371,6 +371,10 @@ INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `de
|
||||
SELECT 'metal_type', 'طلای آبشده', 'عنوان فلز در UI', 0, 1, NULL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'metal_type' LIMIT 1);
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||
SELECT 'main_metal', 'طلا', 'نام فلز اصلی (ادمین)', 0, 1, NULL, 'text'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'main_metal' LIMIT 1);
|
||||
|
||||
-- درگاه — کلیدهای اختیاری payping
|
||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
|
||||
SELECT 'payping_fin_sync_enabled', 'false', 'همگامسازی fin', 1, 1, NULL
|
||||
@@ -666,7 +670,7 @@ UPDATE `settings` SET `type` = 'text' WHERE `key` IN (
|
||||
'node_client_id', 'node_shared_secret',
|
||||
'sse_path',
|
||||
'tahesab_bank_account', 'telegram_bot_token', 'bale_bot_token', 'support_messenger_id',
|
||||
'referral_bonus_amount', 'balance_report_exclude_customer_id', 'metal_type', 'montify_name', 'price_extreme_date',
|
||||
'referral_bonus_amount', 'balance_report_exclude_customer_id', 'metal_type', 'main_metal', 'montify_name', 'price_extreme_date',
|
||||
'default_vc_number', 'liman_token'
|
||||
);
|
||||
|
||||
@@ -922,6 +926,7 @@ UPDATE `settings` SET `description` = 'آدرس تصویر لوگو در صفح
|
||||
UPDATE `settings` SET `description` = 'عنوان کوتاه برای نصب PWA روی موبایل. از دیپلوی مدیریت میشود.' WHERE `key` = 'title_pwa';
|
||||
UPDATE `settings` SET `description` = 'عنوان نمایشدادهشده در تب مرورگر (favicon). از دیپلوی مدیریت میشود.' WHERE `key` = 'title_fav';
|
||||
UPDATE `settings` SET `description` = 'عنوان فلز اصلی در رابط کاربر (مثلاً «طلای آبشده» یا «کاتد مس»). در دارایی، کیف پول و برچسبهای معامله دیده میشود. فقط از دیپلوی/SQL تغییر میکند.' WHERE `key` = 'metal_type';
|
||||
UPDATE `settings` SET `title_fa` = 'نام فلز اصلی (ادمین)', `description` = 'نام کوتاه فلز برای برچسبهای پنل ادمین (مثلاً «مس» یا «طلا»). «تراز طلایی» میشود «تراز مس» — نه «تراز مسی». فقط از دیپلوی/SQL تغییر میکند.' WHERE `key` = 'main_metal';
|
||||
UPDATE `settings` SET `description` = 'واحد نمایش وزن در داراییها، کیف پول و جزئیات معاملات|گرم,کیلوگرم' WHERE `key` = 'unit';
|
||||
UPDATE `settings` SET `description` = 'روشن: کاربران جدید میتوانند ثبتنام کنند. خاموش: فقط ورود کاربران موجود مجاز است.' WHERE `key` = 'open_register';
|
||||
UPDATE `settings` SET `description` = 'روشن: حساب کاربر بلافاصله پس از ثبتنام فعال میشود. خاموش: نیاز به تأیید دستی ادمین دارد.' WHERE `key` = 'auto_active_customer';
|
||||
@@ -1066,7 +1071,7 @@ UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN (
|
||||
'has_page_earn_money',
|
||||
'is_store',
|
||||
'logo_login', 'logo_register', 'title_pwa', 'title_fav',
|
||||
'main_product', 'main_wallet_products', 'metal_type',
|
||||
'main_product', 'main_wallet_products', 'metal_type', 'main_metal',
|
||||
'montify_name',
|
||||
'montzar_base_url', 'montzar_closed',
|
||||
'montzar_diff', 'montzar_last_product_log', 'montzar_live_price', 'montzar_mazaneh',
|
||||
|
||||
@@ -351,6 +351,10 @@ INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `de
|
||||
SELECT 'metal_type', 'طلای آبشده', 'عنوان فلز در UI', 0, 1, NULL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'metal_type' LIMIT 1);
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||
SELECT 'main_metal', 'طلا', 'نام فلز اصلی (ادمین)', 0, 1, NULL, 'text'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'main_metal' LIMIT 1);
|
||||
|
||||
-- درگاه — کلیدهای اختیاری payping
|
||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
|
||||
SELECT 'payping_fin_sync_enabled', 'false', 'همگامسازی fin', 1, 1, NULL
|
||||
@@ -642,7 +646,7 @@ UPDATE `settings` SET `type` = 'text' WHERE `key` IN (
|
||||
'node_client_id', 'node_shared_secret',
|
||||
'sse_path',
|
||||
'tahesab_bank_account', 'telegram_bot_token', 'bale_bot_token', 'support_messenger_id',
|
||||
'referral_bonus_amount', 'balance_report_exclude_customer_id', 'metal_type', 'montify_name', 'price_extreme_date',
|
||||
'referral_bonus_amount', 'balance_report_exclude_customer_id', 'metal_type', 'main_metal', 'montify_name', 'price_extreme_date',
|
||||
'default_vc_number', 'liman_token'
|
||||
);
|
||||
|
||||
@@ -926,6 +930,7 @@ UPDATE `settings` SET `description` = 'آدرس تصویر لوگو در صفح
|
||||
UPDATE `settings` SET `description` = 'عنوان کوتاه برای نصب PWA روی موبایل. از دیپلوی مدیریت میشود.' WHERE `key` = 'title_pwa';
|
||||
UPDATE `settings` SET `description` = 'عنوان نمایشدادهشده در تب مرورگر (favicon). از دیپلوی مدیریت میشود.' WHERE `key` = 'title_fav';
|
||||
UPDATE `settings` SET `description` = 'عنوان فلز اصلی در رابط کاربر (مثلاً «طلای آبشده» یا «کاتد مس»). در دارایی، کیف پول و برچسبهای معامله دیده میشود. فقط از دیپلوی/SQL تغییر میکند.' WHERE `key` = 'metal_type';
|
||||
UPDATE `settings` SET `title_fa` = 'نام فلز اصلی (ادمین)', `description` = 'نام کوتاه فلز برای برچسبهای پنل ادمین (مثلاً «مس» یا «طلا»). «تراز طلایی» میشود «تراز مس» — نه «تراز مسی». فقط از دیپلوی/SQL تغییر میکند.' WHERE `key` = 'main_metal';
|
||||
UPDATE `settings` SET `description` = 'واحد نمایش وزن در داراییها، کیف پول و جزئیات معاملات|گرم,کیلوگرم' WHERE `key` = 'unit';
|
||||
UPDATE `settings` SET `description` = 'روشن: کاربران جدید میتوانند ثبتنام کنند. خاموش: فقط ورود کاربران موجود مجاز است.' WHERE `key` = 'open_register';
|
||||
UPDATE `settings` SET `description` = 'روشن: حساب کاربر بلافاصله پس از ثبتنام فعال میشود. خاموش: نیاز به تأیید دستی ادمین دارد.' WHERE `key` = 'auto_active_customer';
|
||||
|
||||
Reference in New Issue
Block a user