attributes['price_offset_percent']); } if (!static::hasOrderLimitColumns()) { unset( $product->attributes['order_limit_enabled'], $product->attributes['min_order_cash'], $product->attributes['max_order_cash'] ); } if (!static::hasShowInAssetsColumn()) { unset($product->attributes['show_in_assets']); } if (!static::hasUseLastPriceIfZeroColumn()) { unset($product->attributes['use_last_price_if_zero']); } if (!static::hasDirectPriceColumn()) { unset($product->attributes['direct_price']); } if (!static::hasTradeByQuantityColumn()) { unset($product->attributes['trade_by_quantity']); } }); } public static function hasPriceOffsetColumn(): bool { if (static::$priceOffsetColumnExists !== null) { return static::$priceOffsetColumnExists; } try { static::$priceOffsetColumnExists = Schema::hasColumn( (new static)->getTable(), 'price_offset_percent' ); } catch (\Throwable) { static::$priceOffsetColumnExists = false; } return static::$priceOffsetColumnExists; } /** گلد علی: جدول products ممکن است ستون price نداشته باشد */ public static function hasStoredPriceColumns(): bool { if (static::$priceColumnExists !== null) { return static::$priceColumnExists; } try { static::$priceColumnExists = Schema::hasColumn((new static)->getTable(), 'price'); } catch (\Throwable) { static::$priceColumnExists = false; } return static::$priceColumnExists; } public static function hasOrderLimitColumns(): bool { if (static::$orderLimitColumnsExist !== null) { return static::$orderLimitColumnsExist; } try { static::$orderLimitColumnsExist = Schema::hasColumn( (new static)->getTable(), 'order_limit_enabled' ); } catch (\Throwable) { static::$orderLimitColumnsExist = false; } return static::$orderLimitColumnsExist; } public static function hasShowInAssetsColumn(): bool { if (static::$showInAssetsColumnExists !== null) { return static::$showInAssetsColumnExists; } try { static::$showInAssetsColumnExists = Schema::hasColumn( (new static)->getTable(), 'show_in_assets' ); } catch (\Throwable) { static::$showInAssetsColumnExists = false; } return static::$showInAssetsColumnExists; } public static function hasUseLastPriceIfZeroColumn(): bool { if (static::$useLastPriceIfZeroColumnExists !== null) { return static::$useLastPriceIfZeroColumnExists; } try { static::$useLastPriceIfZeroColumnExists = Schema::hasColumn( (new static)->getTable(), 'use_last_price_if_zero' ); } catch (\Throwable) { static::$useLastPriceIfZeroColumnExists = false; } return static::$useLastPriceIfZeroColumnExists; } public static function hasDirectPriceColumn(): bool { if (static::$directPriceColumnExists !== null) { return static::$directPriceColumnExists; } try { static::$directPriceColumnExists = Schema::hasColumn( (new static)->getTable(), 'direct_price' ); } catch (\Throwable) { static::$directPriceColumnExists = false; } return static::$directPriceColumnExists; } public static function hasTradeByQuantityColumn(): bool { if (static::$tradeByQuantityColumnExists !== null) { return static::$tradeByQuantityColumnExists; } try { static::$tradeByQuantityColumnExists = Schema::hasColumn( (new static)->getTable(), 'trade_by_quantity' ); } catch (\Throwable) { static::$tradeByQuantityColumnExists = false; } return static::$tradeByQuantityColumnExists; } public function orderLimitsEnabled(): bool { if (!static::hasOrderLimitColumns() || !($this->order_limit_enabled ?? false)) { return false; } return $this->min_order_cash !== null || $this->max_order_cash !== null; } public function validateOrderCashLimit(float $totalPriceRial): void { if (!$this->orderLimitsEnabled()) { return; } $min = $this->min_order_cash; $max = $this->max_order_cash; if ($min !== null && $totalPriceRial < (float) $min) { throw new \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException( 'حداقل مبلغ این محصول ' . number_format((float) $min) . ' ریال می‌باشد' ); } if ($max !== null && $totalPriceRial > (float) $max) { throw new \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException( 'حداکثر مبلغ این محصول ' . number_format((float) $max) . ' ریال می‌باشد' ); } } public function getPriceOffsetPercentAttribute($value): float { if (!static::hasPriceOffsetColumn()) { return 0.0; } return (float) ($value ?? 0); } protected $fillable = [ 'title', // The title of the record. 'price', // The amount of the record. 'amount', // The amount of the record. 'unit', // The unit of the item. 'group_type', // The unit of the item. 'file_id', // The ID of the associated file. 'can_canceled', // Whether the record can be canceled (boolean). 'active', // The active status of the record (boolean). 'for_barayand', // Whether it is for Barayand (boolean). 'show', // Whether to show the record (boolean). 'show_in_assets', // Show balance on customer assets page cards 'trade_by_quantity', // UI معامله مقداری: ارزش کل + مقدار 'use_last_price_if_zero', // Keep last DB price when automatic provider returns zero 'open_sell', // Whether selling is open (boolean). 'auto_open_at', // Whether selling is open (boolean). 'auto_close_at', // Whether selling is open (boolean). 'kalhor_name', // The name of the Kalhor entity. 'default_credit', // Default credit amount. 'reading_method', // The method of reading. 'open_buy', // Whether buying is open (boolean). '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 'percent_fee_buy', // Add percent_fee_buy to fillable 'order_limit_enabled', 'min_order_cash', 'max_order_cash', 'metal_id', //the material of metal (#gold or #silver) 'price_offset_percent', // admin only: +/- % vs automatic reference (baked into price on sync) 'price_profit', 'bubble', 'inventory', 'description', 'direct_price', ]; protected $attributes = [ 'show_in_assets' => false, 'trade_by_quantity' => false, 'use_last_price_if_zero' => false, ]; protected $casts = [ 'can_canceled' => 'boolean', // Cast to boolean. 'active' => 'boolean', // Cast to boolean. 'show' => 'boolean', // Cast to boolean. 'show_in_assets' => 'boolean', 'trade_by_quantity' => 'boolean', 'use_last_price_if_zero' => 'boolean', 'for_barayand' => 'boolean', // Cast to boolean. 'open_sell' => 'boolean', // Cast to boolean. 'auto_sell' => 'boolean', // Cast to boolean (you mentioned this in your casts but it's missing in `$fillable`). 'open_buy' => 'boolean', // Cast to boolean. 'auto_buy' => 'boolean', // Cast to boolean. 'price' => 'int', // Cast to float. 'percent_fee_sell' => 'float', // Cast to float. 'percent_fee_buy' => 'float', // Cast to float. 'order_limit_enabled' => 'boolean', 'min_order_cash' => 'int', '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 'price_offset_percent' => 'float', 'price_profit' => 'int', ]; static $METAL_ID = [ 1=>'طلا', 2=>'نقره', ]; public function wallet() { return $this->hasOne(Wallet::class); } public function walletMy() { return $this->hasOne(Wallet::class)->where('owner',auth()->user()->id); } public function file(){ return $this->belongsTo(File::class); } public function price(){ return $this->hasOne(Price::class)->orderBy('id','desc'); } public function getPrices() { $now = Carbon::now(); $lastMonth = Carbon::now()->subMonth(); $subQuery = DB::table('prices') ->select('product_id', 'date_at', 'price') ->whereBetween('date_at', [$lastMonth, $now]) ->where('product_id', $this->id) ->orderBy('date_at', 'desc') ->distinct('date_at') ->groupBy('date_at', 'product_id', 'price'); return DB::table(DB::raw("({$subQuery->toSql()}) as sub")) ->mergeBindings($subQuery->getQuery()) ->get(['product_id', 'date_at', 'price']); } public function prices(){ $now = Carbon::now(); $lastMonth = Carbon::now()->subMonth(); $data = []; $datas= $this->hasMany(Price::class, 'product_id') ->whereBetween('date_at', [$lastMonth, $now]) ->distinct('date_at') ->select(['product_id', 'date_at', 'difference', 'price']); return $datas; } public function default(){ return $this->hasMany(ProductCredit::class); } public function sub(){ return $this->hasOne(PackageProduct::class)->where('is_base',true); } // public function wallet(){ // return $this->hasOne(Wallet::class); // } public function transaction(){ return $this->hasMany(Transaction::class); } public function limitedTransactions() { return $this->hasMany(Transaction::class)->select('id', 'gold', 'title', 'created_at', 'product_id'); } public function transactionAgentPanel(){ return $this->hasMany(Transaction::class)->where('note','برداشت بابت فروش')->orwhere('note','واریز بابت خرید')->orwhere('note','سند ورود طلا'); } }