Files
xin-procurement/app/Models/ProductPriceModel.php
T
2026-08-06 14:52:56 +08:00

109 lines
3.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* 商品价格模型(同一商品按客户等级定价,联合键 product_id + level_id
*
* 计价类型:固定价(price 即实际单价)或成本百分比(实际单价 = 成本价 × (100 + percent) / 100
*/
class ProductPriceModel extends Model
{
use HasFactory;
/** 计价类型:固定价 */
public const int PRICE_TYPE_FIXED = 0;
/** 计价类型:成本百分比(按成本价上浮 percent 百分点) */
public const int PRICE_TYPE_PERCENT = 1;
protected $table = 'product_price';
protected $primaryKey = 'id';
protected $fillable = [
'product_id',
'level_id',
'price',
'price_type',
'percent',
];
protected $casts = [
'product_id' => 'integer',
'level_id' => 'integer',
'price' => 'decimal:2',
'price_type' => 'integer',
'percent' => 'decimal:2',
];
/** 序列化时附带实际销售价(后台列表/小程序列表直接展示) */
protected $appends = ['actual_price'];
/**
* 所属商品
*/
public function product(): BelongsTo
{
return $this->belongsTo(ProductModel::class, 'product_id', 'id');
}
/**
* 所属客户等级
*/
public function level(): BelongsTo
{
return $this->belongsTo(CustomerLevelModel::class, 'level_id', 'id');
}
/**
* 按商品 + 等级筛选价格
*/
public function scopeForProductLevel($query, int $productId, int $levelId)
{
return $query->where('product_id', $productId)->where('level_id', $levelId);
}
/**
* 计算实际销售价(统一换算入口,金额走 bcmath 保证两位小数精度)
*
* 固定价返回 price 原值;成本百分比返回 cost × (100 + percent) / 100(四舍五入保留两位)。
*
* @param string|int|float $price 固定价(decimal cast 后为 '5.50' 形式字符串)
* @param string|int|float $percent 成本上浮百分点(30 = 上浮 30%)
* @param string|int|float $costPrice 商品成本价(decimal cast 字符串)
* @return string 两位小数字符串,如 '13.05'
*/
public static function calcActualPrice(
int $priceType,
string|int|float $price,
string|int|float $percent,
string|int|float $costPrice,
): string {
if ($priceType === self::PRICE_TYPE_PERCENT) {
$multiplier = bcadd('100', (string) $percent, 4);
return bcdiv(bcmul((string) $costPrice, $multiplier, 4), '100', 2);
}
// 固定价:归一化为两位小数字符串
return bcadd((string) $price, '0', 2);
}
/**
* 实际销售价访问器(供 toArray 输出 actual_price
*
* 依赖 product 关系取成本价;prices 经商品 eager load 加载时逆向关系自动填充,无 N+1。
* 注意:单独序列化本模型且未加载 product 关系时会触发一次查询,成本价缺失按 0 兜底。
*/
protected function getActualPriceAttribute(): string
{
return self::calcActualPrice(
(int) $this->price_type,
(string) $this->price,
(string) $this->percent,
(string) ($this->product?->cost_price ?? 0),
);
}
}