From 4ef0dfb2c0dd1e30d2bc1395a49f2cdf592e8cc3 Mon Sep 17 00:00:00 2001 From: adonis Date: Sat, 4 Jul 2026 18:00:57 +0330 Subject: [PATCH] added percent in product price --- .../Controllers/Customer/CartController.php | 13 +++-- .../Single/SingleOrderController.php | 7 ++- app/Models/Product.php | 34 ++++++++++++ app/Services/ProductPriceService.php | 43 +++++++++++++++ ...ash_fee_percent_mode_to_products_table.php | 53 +++++++++++++++++++ database/sql/deploy_settings_const.sql | 9 +++- ...ngs_const_preserve_value_sync_can_edit.sql | 8 ++- 7 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 database/migrations/2026_07_04_000001_add_cash_fee_percent_mode_to_products_table.php diff --git a/app/Http/Controllers/Customer/CartController.php b/app/Http/Controllers/Customer/CartController.php index 9a4f42f..a70db0e 100644 --- a/app/Http/Controllers/Customer/CartController.php +++ b/app/Http/Controllers/Customer/CartController.php @@ -18,6 +18,7 @@ use App\Models\Price; use App\Http\Controllers\Admin\ProductController; use App\Http\Controllers\Admin\GiftCardController; use App\Services\ConfigService; +use App\Services\ProductPriceService; @@ -243,11 +244,16 @@ class CartController extends Controller //محاسبه مجموع درصد کارمزد سبد با احتساب تعداد $data['service_charge_percentage'] += $product->percent_fee_buy * $item->quantity; //محاسبه مجموع کارمزد ثابت سبد با احتساب تعداد - $data['fee'] += $product->cash_fee_buy * $item->quantity; + $unitCashFee = ProductPriceService::resolveCashFeeRial( + $product, + 1, + max((float) $goldPrice, ProductPriceService::productBasePriceRial($product)) + ); + $data['fee'] += $unitCashFee * $item->quantity; // قیمت نهایی هر گرم $priceWithFee = $goldPrice + $data['fee']; //محاسبه مجموع کارمزد ثابت سبد با احتساب تعداد - $product_fees += $product->cash_fee_buy; + $product_fees += $unitCashFee; // قیمت آیتم $itemTotalPrice = $finalAmount * $priceWithFee; @@ -375,7 +381,8 @@ class CartController extends Controller throw new UnprocessableEntityHttpException("قیمت محصول {$product->title} مشخص نیست."); } - $cashFee = (float) ($product->cash_fee_buy ?? 0) * $qty; + $unitCashFee = ProductPriceService::resolveCashFeeRial($product, 1, $unitPrice); + $cashFee = $unitCashFee * $qty; $lineTotal = ($unitPrice * $qty) + $cashFee; $total_price += $lineTotal; diff --git a/app/Http/Controllers/Single/SingleOrderController.php b/app/Http/Controllers/Single/SingleOrderController.php index b83867a..c85e697 100644 --- a/app/Http/Controllers/Single/SingleOrderController.php +++ b/app/Http/Controllers/Single/SingleOrderController.php @@ -25,6 +25,7 @@ use App\Models\Setting; use App\Models\Cnt; use App\Services\MontzarService; use App\Services\ConfigService; +use App\Services\ProductPriceService; use App\Services\ProductDisplayPriceService; use App\Services\ReferralGiftService; use App\Services\WalletBroadcastService; @@ -1057,7 +1058,11 @@ class SingleOrderController extends Controller } } - return (float) ($direction === 1 ? $product->cash_fee_buy : $product->cash_fee_sell); + return ProductPriceService::resolveCashFeeRial( + $product, + $direction, + ProductPriceService::productBasePriceRial($product) + ); } private function bonusGift($customer, $referrer) diff --git a/app/Models/Product.php b/app/Models/Product.php index 801af97..96c74a3 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -24,6 +24,7 @@ class Product extends Model private static ?bool $tradeByQuantityColumnExists = null; private static ?bool $inventoryColumnExists = null; private static ?bool $overrideTradingHoursColumnExists = null; + private static ?bool $cashFeePercentModeColumnsExist = null; protected static function booted(): void { @@ -56,6 +57,12 @@ class Product extends Model if (!static::hasOverrideTradingHoursColumn()) { unset($product->attributes['override_trading_hours']); } + if (!static::hasCashFeePercentModeColumns()) { + unset( + $product->attributes['cash_fee_buy_is_percent'], + $product->attributes['cash_fee_sell_is_percent'] + ); + } }); } @@ -77,6 +84,27 @@ class Product extends Model return static::$overrideTradingHoursColumnExists; } + public static function hasCashFeePercentModeColumns(): bool + { + if (static::$cashFeePercentModeColumnsExist !== null) { + return static::$cashFeePercentModeColumnsExist; + } + + try { + static::$cashFeePercentModeColumnsExist = Schema::hasColumn( + (new static)->getTable(), + 'cash_fee_buy_is_percent' + ) && Schema::hasColumn( + (new static)->getTable(), + 'cash_fee_sell_is_percent' + ); + } catch (\Throwable) { + static::$cashFeePercentModeColumnsExist = false; + } + + return static::$cashFeePercentModeColumnsExist; + } + public static function hasPriceOffsetColumn(): bool { if (static::$priceOffsetColumnExists !== null) { @@ -283,6 +311,8 @@ class Product extends Model 'percent_fee_sell', // Add percent_fee_sell to fillable 'cash_fee_buy', // Add cash_fee_buy to fillable 'cash_fee_sell', // Add cash_fee_sell to fillable + 'cash_fee_buy_is_percent', + 'cash_fee_sell_is_percent', 'percent_fee_buy', // Add percent_fee_buy to fillable 'order_limit_enabled', 'min_order_cash', @@ -300,6 +330,8 @@ class Product extends Model 'show_in_assets' => false, 'trade_by_quantity' => false, 'use_last_price_if_zero' => false, + 'cash_fee_buy_is_percent' => false, + 'cash_fee_sell_is_percent' => false, ]; protected $casts = [ @@ -323,6 +355,8 @@ class Product extends Model 'max_order_cash' => 'int', 'cash_fee_buy' => 'int', // Cast to int or decimal if needed 'cash_fee_sell' => 'int', // Cast to int or decimal if needed + 'cash_fee_buy_is_percent' => 'boolean', + 'cash_fee_sell_is_percent' => 'boolean', 'price_offset_percent' => 'float', 'price_profit' => 'int', ]; diff --git a/app/Services/ProductPriceService.php b/app/Services/ProductPriceService.php index ea536d1..b30ede5 100644 --- a/app/Services/ProductPriceService.php +++ b/app/Services/ProductPriceService.php @@ -6,6 +6,49 @@ use App\Models\Product; class ProductPriceService { + /** 5 => 5% ، 0.05 => 5% (اعشاری) */ + public static function feePercentRatio(float $percentValue): float + { + if ($percentValue == 0.0) { + return 0.0; + } + + if (abs($percentValue) > 0 && abs($percentValue) < 1) { + return $percentValue; + } + + return $percentValue / 100; + } + + public static function resolveCashFeeRial(Product $product, int $direction, float $basePriceRial): float + { + $isBuy = $direction === 1; + $rawFee = (float) ($isBuy ? ($product->cash_fee_buy ?? 0) : ($product->cash_fee_sell ?? 0)); + + if ($rawFee == 0.0) { + return 0.0; + } + + $isPercent = $isBuy + ? (bool) ($product->cash_fee_buy_is_percent ?? false) + : (bool) ($product->cash_fee_sell_is_percent ?? false); + + if (! $isPercent || ! Product::hasCashFeePercentModeColumns()) { + return $rawFee; + } + + if ($basePriceRial <= 0) { + return 0.0; + } + + return round(abs($basePriceRial * self::feePercentRatio($rawFee))); + } + + public static function productBasePriceRial(Product $product): float + { + return (float) (($product->price ?? 0) + ($product->price_profit ?? 0)); + } + public static function referenceTotal(int $basePrice, int $baseProfit = 0): int { return $basePrice + $baseProfit; diff --git a/database/migrations/2026_07_04_000001_add_cash_fee_percent_mode_to_products_table.php b/database/migrations/2026_07_04_000001_add_cash_fee_percent_mode_to_products_table.php new file mode 100644 index 0000000..c038478 --- /dev/null +++ b/database/migrations/2026_07_04_000001_add_cash_fee_percent_mode_to_products_table.php @@ -0,0 +1,53 @@ +boolean('cash_fee_buy_is_percent')->default(false)->after('cash_fee_buy'); + } else { + $table->boolean('cash_fee_buy_is_percent')->default(false); + } + } + + if (! Schema::hasColumn('products', 'cash_fee_sell_is_percent')) { + if (Schema::hasColumn('products', 'cash_fee_sell')) { + $table->boolean('cash_fee_sell_is_percent')->default(false)->after('cash_fee_sell'); + } else { + $table->boolean('cash_fee_sell_is_percent')->default(false); + } + } + }); + } + + public function down(): void + { + if (! Schema::hasTable('products')) { + return; + } + + Schema::table('products', function (Blueprint $table) { + $drops = []; + if (Schema::hasColumn('products', 'cash_fee_buy_is_percent')) { + $drops[] = 'cash_fee_buy_is_percent'; + } + if (Schema::hasColumn('products', 'cash_fee_sell_is_percent')) { + $drops[] = 'cash_fee_sell_is_percent'; + } + if ($drops !== []) { + $table->dropColumn($drops); + } + }); + } +}; diff --git a/database/sql/deploy_settings_const.sql b/database/sql/deploy_settings_const.sql index b9d7a6f..d0581d6 100644 --- a/database/sql/deploy_settings_const.sql +++ b/database/sql/deploy_settings_const.sql @@ -287,6 +287,10 @@ INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `de SELECT 'group_sms', 'false', 'پیامک گروهی', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'group_sms' LIMIT 1); +INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`) +SELECT 'sms_panel', 'true', 'پنل پیامک (کیف پول)', 0, 1, 'روشن: پنل پیامک و کیف پول billing فعال است. خاموش: پیامک‌ها مثل قبل ارسال می‌شوند بدون billing.', 'switch' +WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'sms_panel' LIMIT 1); + INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) SELECT 'tahesab_host', '', 'آدرس IP سرور ته‌حساب', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'tahesab_host' LIMIT 1); @@ -608,7 +612,7 @@ UPDATE `settings` SET `type` = 'switch' WHERE `key` IN ( 'close_price', 'show_last_price', 'fee_deduct_from_gold', 'send_to_tahesab', 'profit_gold_api_enabled', 'payping_round_toman', 'payping_strict_failed_withdraw', - 'has_page_earn_money', 'charge_notif', 'group_sms', + 'has_page_earn_money', 'charge_notif', 'group_sms', 'sms_panel', 'montzar_closed', 'montzar_open_buy', 'montzar_open_sell', 'payping_fin_sync_enabled', 'external_order_api_enabled', 'is_store', 'wss_enabled', 'register_referral_sms_to_referrer', 'register_referral_bonus_message', @@ -988,6 +992,7 @@ UPDATE `settings` SET `description` = 'توکن ربات بله برای ارس UPDATE `settings` SET `description` = 'روشن: پس از شارژ کیف توسط ادمین، پیامک به مشتری ارسال می‌شود.' WHERE `key` = 'charge_notif'; UPDATE `settings` SET `description` = 'روشن: امکان ارسال پیامک گروهی از پنل ادمین فعال است.' WHERE `key` = 'group_sms'; +UPDATE `settings` SET `description` = 'روشن: پنل پیامک و کیف پول billing فعال است. خاموش: پیامک‌ها مثل قبل ارسال می‌شوند بدون billing.' WHERE `key` = 'sms_panel'; UPDATE `settings` SET `description` = 'محصولاتی که موجودی‌شان از کیف طلای اصلی خوانده می‌شود. چند محصول با کاما. پیش‌فرض: 13' WHERE `key` = 'main_wallet_products'; UPDATE `settings` SET `value` = '13' WHERE `key` = 'main_wallet_products' AND TRIM(COALESCE(`value`, '')) = ''; @@ -1044,6 +1049,7 @@ UPDATE `settings` SET `title_fa` = 'اختلاف پایه نمایش' WHERE `key UPDATE `settings` SET `title_fa` = 'پاداش معرف‌شونده' WHERE `key` = 'gift_for_referral'; UPDATE `settings` SET `title_fa` = 'پاداش معرف' WHERE `key` = 'gift_for_referrer'; UPDATE `settings` SET `title_fa` = 'پیامک گروهی' WHERE `key` = 'group_sms'; +UPDATE `settings` SET `title_fa` = 'پنل پیامک (کیف پول)' WHERE `key` = 'sms_panel'; UPDATE `settings` SET `title_fa` = 'چت پشتیبانی' WHERE `key` = 'has_chat'; UPDATE `settings` SET `title_fa` = 'صفحه کسب درآمد' WHERE `key` = 'has_page_earn_money'; UPDATE `settings` SET `title_fa` = 'صفحه صندوق' WHERE `key` = 'has_page_fund'; @@ -1147,6 +1153,7 @@ UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN ( 'group_sms', 'has_page_earn_money', 'is_store', + 'sms_panel', 'logo_login', 'logo_register', 'title_pwa', 'title_fav', 'main_product', 'main_wallet_products', 'metal_type', 'main_metal', 'montify_name', diff --git a/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql b/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql index 5157a88..cb5df43 100644 --- a/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql +++ b/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql @@ -283,6 +283,10 @@ INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `de SELECT 'group_sms', 'false', 'پیامک گروهی', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'group_sms' LIMIT 1); +INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`) +SELECT 'sms_panel', 'true', 'پنل پیامک (کیف پول)', 0, 1, 'روشن: پنل پیامک و کیف پول billing فعال است. خاموش: پیامک‌ها مثل قبل ارسال می‌شوند بدون billing.', 'switch' +WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'sms_panel' LIMIT 1); + INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) SELECT 'tahesab_host', '', 'آدرس IP سرور ته‌حساب', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'tahesab_host' LIMIT 1); @@ -605,7 +609,7 @@ UPDATE `settings` SET `type` = 'switch' WHERE `key` IN ( 'close_price', 'show_last_price', 'fee_deduct_from_gold', 'send_to_tahesab', 'profit_gold_api_enabled', 'payping_round_toman', 'payping_strict_failed_withdraw', - 'has_page_earn_money', 'charge_notif', 'group_sms', + 'has_page_earn_money', 'charge_notif', 'group_sms', 'sms_panel', 'montzar_closed', 'montzar_open_buy', 'montzar_open_sell', 'payping_fin_sync_enabled', 'external_order_api_enabled', 'is_store', 'wss_enabled', 'register_referral_sms_to_referrer', 'register_referral_bonus_message', @@ -1013,6 +1017,7 @@ UPDATE `settings` SET `description` = 'توکن ربات بله برای ارس UPDATE `settings` SET `description` = 'روشن: پس از شارژ کیف توسط ادمین، پیامک به مشتری ارسال می‌شود.' WHERE `key` = 'charge_notif'; UPDATE `settings` SET `description` = 'روشن: امکان ارسال پیامک گروهی از پنل ادمین فعال است.' WHERE `key` = 'group_sms'; +UPDATE `settings` SET `description` = 'روشن: پنل پیامک و کیف پول billing فعال است. خاموش: پیامک‌ها مثل قبل ارسال می‌شوند بدون billing.' WHERE `key` = 'sms_panel'; UPDATE `settings` SET `description` = 'محصولاتی که موجودی‌شان از کیف طلای اصلی خوانده می‌شود. چند محصول با کاما. پیش‌فرض: 13' WHERE `key` = 'main_wallet_products'; UPDATE `settings` SET `value` = '13' WHERE `key` = 'main_wallet_products' AND TRIM(COALESCE(`value`, '')) = ''; @@ -1057,6 +1062,7 @@ UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN ( 'group_sms', 'has_page_earn_money', 'is_store', + 'sms_panel', 'logo_login', 'logo_register', 'title_pwa', 'title_fav', 'main_product', 'main_wallet_products', 'metal_type', 'main_metal', 'montify_name',