2026-01-24 14:03:11 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
|
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
|
|
|
use Rooyesh\Wallet\models\Transaction;
|
|
|
|
|
use Rooyesh\Wallet\models\Wallet;
|
|
|
|
|
use Carbon\Carbon;
|
|
|
|
|
use Illuminate\Support\Facades\DB;
|
2026-06-02 16:00:33 +03:30
|
|
|
use Illuminate\Support\Facades\Schema;
|
2026-01-24 14:03:11 +00:00
|
|
|
|
|
|
|
|
class Product extends Model
|
|
|
|
|
{
|
|
|
|
|
use HasFactory,Selector,SoftDeletes;
|
2026-06-02 16:00:33 +03:30
|
|
|
|
|
|
|
|
private static ?bool $priceOffsetColumnExists = null;
|
2026-06-04 14:42:22 +03:30
|
|
|
private static ?bool $priceColumnExists = null;
|
2026-06-06 22:06:53 +03:30
|
|
|
private static ?bool $orderLimitColumnsExist = null;
|
2026-06-09 16:44:02 +03:30
|
|
|
private static ?bool $showInAssetsColumnExists = null;
|
2026-06-10 13:11:51 +03:30
|
|
|
private static ?bool $useLastPriceIfZeroColumnExists = null;
|
2026-06-14 13:31:32 +03:30
|
|
|
private static ?bool $directPriceColumnExists = null;
|
2026-06-17 16:13:18 +03:30
|
|
|
private static ?bool $tradeByQuantityColumnExists = null;
|
2026-06-22 20:09:56 +03:30
|
|
|
private static ?bool $inventoryColumnExists = null;
|
2026-06-28 15:16:39 +03:30
|
|
|
private static ?bool $overrideTradingHoursColumnExists = null;
|
2026-07-04 18:00:57 +03:30
|
|
|
private static ?bool $cashFeePercentModeColumnsExist = null;
|
2026-06-02 16:00:33 +03:30
|
|
|
|
|
|
|
|
protected static function booted(): void
|
|
|
|
|
{
|
|
|
|
|
static::saving(function (Product $product) {
|
|
|
|
|
if (!static::hasPriceOffsetColumn()) {
|
|
|
|
|
unset($product->attributes['price_offset_percent']);
|
|
|
|
|
}
|
2026-06-06 22:06:53 +03:30
|
|
|
if (!static::hasOrderLimitColumns()) {
|
|
|
|
|
unset(
|
|
|
|
|
$product->attributes['order_limit_enabled'],
|
|
|
|
|
$product->attributes['min_order_cash'],
|
|
|
|
|
$product->attributes['max_order_cash']
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-06-09 16:44:02 +03:30
|
|
|
if (!static::hasShowInAssetsColumn()) {
|
|
|
|
|
unset($product->attributes['show_in_assets']);
|
|
|
|
|
}
|
2026-06-10 13:11:51 +03:30
|
|
|
if (!static::hasUseLastPriceIfZeroColumn()) {
|
|
|
|
|
unset($product->attributes['use_last_price_if_zero']);
|
|
|
|
|
}
|
2026-06-14 13:31:32 +03:30
|
|
|
if (!static::hasDirectPriceColumn()) {
|
|
|
|
|
unset($product->attributes['direct_price']);
|
|
|
|
|
}
|
2026-06-17 16:13:18 +03:30
|
|
|
if (!static::hasTradeByQuantityColumn()) {
|
|
|
|
|
unset($product->attributes['trade_by_quantity']);
|
|
|
|
|
}
|
2026-06-22 20:09:56 +03:30
|
|
|
if (!static::hasInventoryColumn()) {
|
|
|
|
|
unset($product->attributes['inventory']);
|
|
|
|
|
}
|
2026-06-28 15:16:39 +03:30
|
|
|
if (!static::hasOverrideTradingHoursColumn()) {
|
|
|
|
|
unset($product->attributes['override_trading_hours']);
|
|
|
|
|
}
|
2026-07-04 18:00:57 +03:30
|
|
|
if (!static::hasCashFeePercentModeColumns()) {
|
|
|
|
|
unset(
|
|
|
|
|
$product->attributes['cash_fee_buy_is_percent'],
|
|
|
|
|
$product->attributes['cash_fee_sell_is_percent']
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-06-02 16:00:33 +03:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-28 15:16:39 +03:30
|
|
|
public static function hasOverrideTradingHoursColumn(): bool
|
|
|
|
|
{
|
|
|
|
|
if (static::$overrideTradingHoursColumnExists !== null) {
|
|
|
|
|
return static::$overrideTradingHoursColumnExists;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
static::$overrideTradingHoursColumnExists = Schema::hasColumn(
|
|
|
|
|
(new static)->getTable(),
|
|
|
|
|
'override_trading_hours'
|
|
|
|
|
);
|
|
|
|
|
} catch (\Throwable) {
|
|
|
|
|
static::$overrideTradingHoursColumnExists = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return static::$overrideTradingHoursColumnExists;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-04 18:00:57 +03:30
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 16:00:33 +03:30
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 14:42:22 +03:30
|
|
|
/** گلد علی: جدول 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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 22:06:53 +03:30
|
|
|
public static function hasOrderLimitColumns(): bool
|
|
|
|
|
{
|
|
|
|
|
if (static::$orderLimitColumnsExist !== null) {
|
|
|
|
|
return static::$orderLimitColumnsExist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2026-07-08 12:45:14 +03:30
|
|
|
$table = (new static)->getTable();
|
|
|
|
|
static::$orderLimitColumnsExist = Schema::hasColumn($table, 'order_limit_enabled')
|
|
|
|
|
&& Schema::hasColumn($table, 'min_order_cash')
|
|
|
|
|
&& Schema::hasColumn($table, 'max_order_cash');
|
2026-06-06 22:06:53 +03:30
|
|
|
} catch (\Throwable) {
|
|
|
|
|
static::$orderLimitColumnsExist = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return static::$orderLimitColumnsExist;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 16:44:02 +03:30
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 13:11:51 +03:30
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-14 13:31:32 +03:30
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 16:13:18 +03:30
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 20:09:56 +03:30
|
|
|
public static function hasInventoryColumn(): bool
|
|
|
|
|
{
|
|
|
|
|
if (static::$inventoryColumnExists !== null) {
|
|
|
|
|
return static::$inventoryColumnExists;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
static::$inventoryColumnExists = Schema::hasColumn(
|
|
|
|
|
(new static)->getTable(),
|
|
|
|
|
'inventory'
|
|
|
|
|
);
|
|
|
|
|
} catch (\Throwable) {
|
|
|
|
|
static::$inventoryColumnExists = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return static::$inventoryColumnExists;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 22:06:53 +03:30
|
|
|
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) . ' ریال میباشد'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 16:00:33 +03:30
|
|
|
public function getPriceOffsetPercentAttribute($value): float
|
|
|
|
|
{
|
|
|
|
|
if (!static::hasPriceOffsetColumn()) {
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (float) ($value ?? 0);
|
|
|
|
|
}
|
2026-01-24 14:03:11 +00:00
|
|
|
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).
|
2026-06-09 16:44:02 +03:30
|
|
|
'show_in_assets', // Show balance on customer assets page cards
|
2026-06-17 16:13:18 +03:30
|
|
|
'trade_by_quantity', // UI معامله مقداری: ارزش کل + مقدار
|
2026-06-10 13:11:51 +03:30
|
|
|
'use_last_price_if_zero', // Keep last DB price when automatic provider returns zero
|
2026-01-24 14:03:11 +00:00
|
|
|
'open_sell', // Whether selling is open (boolean).
|
|
|
|
|
'auto_open_at', // Whether selling is open (boolean).
|
|
|
|
|
'auto_close_at', // Whether selling is open (boolean).
|
2026-06-28 15:16:39 +03:30
|
|
|
'override_trading_hours', // روشن: بازه ساعت خود محصول جایگزین بازه سراسری میشود
|
2026-01-24 14:03:11 +00:00
|
|
|
'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
|
2026-07-04 18:00:57 +03:30
|
|
|
'cash_fee_buy_is_percent',
|
|
|
|
|
'cash_fee_sell_is_percent',
|
2026-01-24 14:03:11 +00:00
|
|
|
'percent_fee_buy', // Add percent_fee_buy to fillable
|
2026-06-06 22:06:53 +03:30
|
|
|
'order_limit_enabled',
|
|
|
|
|
'min_order_cash',
|
|
|
|
|
'max_order_cash',
|
2026-01-24 14:03:11 +00:00
|
|
|
'metal_id', //the material of metal (#gold or #silver)
|
2026-06-02 16:00:33 +03:30
|
|
|
'price_offset_percent', // admin only: +/- % vs automatic reference (baked into price on sync)
|
|
|
|
|
'price_profit',
|
|
|
|
|
'bubble',
|
2026-06-12 20:28:34 +03:30
|
|
|
'inventory',
|
|
|
|
|
'description',
|
|
|
|
|
'direct_price',
|
2026-01-24 14:03:11 +00:00
|
|
|
];
|
|
|
|
|
|
2026-06-10 13:11:51 +03:30
|
|
|
protected $attributes = [
|
|
|
|
|
'show_in_assets' => false,
|
2026-06-17 16:13:18 +03:30
|
|
|
'trade_by_quantity' => false,
|
2026-06-10 13:11:51 +03:30
|
|
|
'use_last_price_if_zero' => false,
|
2026-07-04 18:00:57 +03:30
|
|
|
'cash_fee_buy_is_percent' => false,
|
|
|
|
|
'cash_fee_sell_is_percent' => false,
|
2026-06-10 13:11:51 +03:30
|
|
|
];
|
|
|
|
|
|
2026-01-24 14:03:11 +00:00
|
|
|
protected $casts = [
|
|
|
|
|
'can_canceled' => 'boolean', // Cast to boolean.
|
|
|
|
|
'active' => 'boolean', // Cast to boolean.
|
|
|
|
|
'show' => 'boolean', // Cast to boolean.
|
2026-06-09 16:44:02 +03:30
|
|
|
'show_in_assets' => 'boolean',
|
2026-06-17 16:13:18 +03:30
|
|
|
'trade_by_quantity' => 'boolean',
|
2026-06-10 13:11:51 +03:30
|
|
|
'use_last_price_if_zero' => 'boolean',
|
2026-06-28 15:16:39 +03:30
|
|
|
'override_trading_hours' => 'boolean',
|
2026-01-24 14:03:11 +00:00
|
|
|
'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.
|
2026-06-06 22:06:53 +03:30
|
|
|
'order_limit_enabled' => 'boolean',
|
|
|
|
|
'min_order_cash' => 'int',
|
|
|
|
|
'max_order_cash' => 'int',
|
2026-07-04 22:06:20 +03:30
|
|
|
'cash_fee_buy' => 'float',
|
|
|
|
|
'cash_fee_sell' => 'float',
|
2026-07-04 18:00:57 +03:30
|
|
|
'cash_fee_buy_is_percent' => 'boolean',
|
|
|
|
|
'cash_fee_sell_is_percent' => 'boolean',
|
2026-06-02 16:00:33 +03:30
|
|
|
'price_offset_percent' => 'float',
|
|
|
|
|
'price_profit' => 'int',
|
2026-01-24 14:03:11 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
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(){
|
2026-05-17 16:39:53 +00:00
|
|
|
return $this->belongsTo(File::class);
|
2026-01-24 14:03:11 +00:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
2026-06-04 12:18:53 +03:30
|
|
|
|
|
|
|
|
public function limitedTransactions()
|
|
|
|
|
{
|
|
|
|
|
return $this->hasMany(Transaction::class)->select('id', 'gold', 'title', 'created_at', 'product_id');
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 14:03:11 +00:00
|
|
|
public function transactionAgentPanel(){
|
|
|
|
|
return $this->hasMany(Transaction::class)->where('note','برداشت بابت فروش')->orwhere('note','واریز بابت خرید')->orwhere('note','سند ورود طلا');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|