ldc-boutique-cnc/app/Models/Product.php
Jérémy Munsch f99e259bd5
Tons of fixes Refonte SYNC 1
- Authentification System OK
2022-11-16 12:47:41 +01:00

897 lines
30 KiB
PHP

<?php
namespace App\Models;
use App\Enums\DefaultProductType;
use App\Enums\GeneralConfigType;
use App\Enums\InvoicingUnit;
use App\Enums\SellUnit;
use App\Enums\TaxRate;
use App\Enums\WeightUnit;
use App\Lib\Helpers\FileStorageHelper;
use App\Lib\Helpers\ToolboxHelper;
use App\Traits\Models\MiniCrudModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Gloudemans\Shoppingcart\Contracts\Buyable;
use Gloudemans\Shoppingcart\CanBeBought;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Kwaadpepper\Enum\Traits\CastsEnums;
/**
* Product
*
* By default engine supports product base and inco products
*
* @property float priceHt Base Price
* @property float discountedPriceHt Base Price after discount
* @property float priceTtc Price VAT included
* @property float discountedPriceTtc|string Price after discount VAT included
* @property float discountAmount Discount amount
* @property float discountRate Discount rate
* @property array data The overlapped data like defined in template.
* @property array dataObject The overlapped data like defined in template as object.
*
* @package App\Models
*/
class Product extends Model implements Buyable
{
use CanBeBought;
use CastsEnums;
use HasFactory;
use MiniCrudModel;
use SoftDeletes;
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'star' => 'bool',
'status' => 'bool',
'orderable' => 'bool',
'publish_at' => 'datetime',
'unpublish_at' => 'datetime',
'orderable_start' => 'datetime',
'orderable_end' => 'datetime',
'data' => 'array'
];
/**
* The attributes that should be cast to Enums.
*
* @var array
*/
protected $enumCasts = [
'tax_rate' => TaxRate::class,
];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'reference', 'type',
'name', 'description',
'picture', 'price',
'sell_unit', 'weight',
'tax_rate', 'star',
'status', 'orderable',
'orderable_at', 'unorderable_at',
'publish_at', 'unpublish_at',
'data', 'range_id',
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted()
{
// * EVENT CLOSURES.
static::creating(function (self $model) {
// * ASSERTIONS
static::assertNameIsUnique($model->name);
ToolboxHelper::assertNoInternalBoUrls('description', $model->description);
self::assertIsAboveZero('price', trans('Prix'), $model->price);
self::assertIsAboveZero('weight', trans('Poids'), $model->weight);
// * TRANSFORMATIONS
$model->reference = $model->reference ?: ToolboxHelper::getUniqueProductReference();
$model->slug = Str::slug($model->name);
OldSlugable::removeSameSlugs($model, $model->slug);
$model->description = clean($model->description);
$model->description = FileStorageHelper::storeModelFilesFromHtml($model, $model->description);
if ($model->picture) {
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
}
$model->makeStatusSense();
$model->makeOrderableSense();
$model->minicrudCreating();
});
static::created(function (self $model) {
$model->flashMessages();
});
static::updating(function (self $model) {
// * ASSERTIONS
static::assertNameIsUnique($model->name, $model->id);
ToolboxHelper::assertNoInternalBoUrls('description', $model->description);
self::assertIsAboveZero('price', trans('Prix'), $model->price);
self::assertIsAboveZero('weight', trans('Poids'), $model->weight);
// * TRANSFORMATIONS
$model->reference = $model->reference ?: ToolboxHelper::getUniqueProductReference();
$model->slug = Str::slug($model->name);
if ($model->slug != $model->getOriginal('slug')) {
OldSlugable::removeSameSlugs($model, $model->getOriginal('slug'));
}
OldSlugable::insertSlug($model);
$model->description = clean($model->description);
$model->description = FileStorageHelper::storeModelFilesFromHtml($model, $model->description);
if ($model->picture) {
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
}
$model->makeStatusSense();
$model->makeOrderableSense();
$model->minicrudUpdating();
});
static::updated(function (self $model) {
FileStorageHelper::removeOldFile($model, 'picture');
FileStorageHelper::removeOldModelFilesFromHtml($model->description, $model->getOriginal('description'));
$model->flashMessages();
$model->minicrudUpdated();
});
static::deleted(function (self $model) {
$model->oldSlugs()->delete();
FileStorageHelper::removeFile($model, 'picture');
FileStorageHelper::removeModelFilesOfHtml($model->description);
$model->minicrudDeleted();
});
}
// * RELATIONSHIPS
/**
* BelongsToMany products relation
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function associatedProducts()
{
return $this->belongsToMany(self::class, 'product_product', 'associated_id', 'product_id');
}
/**
* BelongsTo range relation
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function range()
{
return $this->belongsTo(Range::class);
}
/**
* MorphMany old_slugable relation
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function oldSlugs()
{
return $this->morphMany(OldSlugable::class, 'old_slugable');
}
/**
* belongsToMany discounts relation
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function discounts()
{
return $this->belongsToMany(Discount::class, 'product_discount');
}
/**
* belongsToMany tags relation
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
return $this->belongsToMany(Tag::class);
}
/**
* belongsTo xml_inco relation
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function xmlInco()
{
return $this->belongsTo(XmlInco::class);
}
// * METHODS
/**
* Flash boolean attributes modification messages
*
* @param boolean $old Ignore old attribute comparison.
* @return void
*/
public function flashMessages(bool $old = true)
{
$booleanChecks = [
'status' => [
'info' => trans(':model est maintenant publié', [
'model' => Str::singular(trans('models.classes.products'))
]),
'warning' => trans(':model est maintenant dépublié', [
'model' => Str::singular(trans('models.classes.products'))
])
],
'star' => [
'info' => trans(':model est maintenant mis en avant', [
'model' => Str::singular(trans('models.classes.products'))
]),
'warning' => trans(':model n\'est maintenant plus mis en avant', [
'model' => Str::singular(trans('models.classes.products'))
])
],
'orderable' => [
'warning' => trans(':model est maintenant disponible à la vente', [
'model' => Str::singular(trans('models.classes.products'))
]),
'error' => trans(':model n\'est maintenant plus disponible à la vente', [
'model' => Str::singular(trans('models.classes.products'))
])
]
];
$sessionMessages = [];
// * Merge messages
foreach ($booleanChecks as $attribute => $booleanCheck) {
if ($old and $this->getOriginal($attribute) == $this->{$attribute}) {
continue;
}
/** @var string $flashType */
$flashType = \array_keys($booleanCheck)[$this->{$attribute} ? 0 : 1];
if (!\array_key_exists($flashType, $sessionMessages)) {
$sessionMessages[$flashType] = [];
}
$sessionMessages[$flashType][] = $booleanCheck[$flashType];
}
foreach ($sessionMessages as $flashType => $messages) {
session()->flash($flashType, collect($messages)->implode("\n"));
}
}
/**
* Check this product an inco sheet associated and its type is inco
* @return boolean
*/
public function hasAndIsInco(): bool
{
return $this->type->value === DefaultProductType::inco()->value and
$this->xmlInco;
}
/**
* Getter data_object, the object overloaded attributes from theme
*
* @return object
*/
public function getDataObjectAttribute(): object
{
return ToolboxHelper::arrayToObject($this->data);
}
/**
* Get inco nutritionnal values, if product is inco.
*
* @return object|null
*/
public function getIncoNutritionalValues(): ?object
{
if (!$this->hasAndIsInco()) {
return null;
}
$nutri_values = $this->xmlInco->nutritional_values;
if (empty($nutri_values)) {
return null;
}
return (object)[
// Pour la ref.
'nv_ref_weight_g' => $nutri_values['taille_ref'],
// Energie Kj.
'nv_kj' => $nutri_values['val_energ_kj_r'],
// Energie Kcal.
'nv_kcal' => $nutri_values['val_energ_kcal_r'],
// Graisses.
'nv_fat_g' => $nutri_values['mat_grass_r'],
// Dont acides gras saturés.
'nv_sat_fat_g' => $nutri_values['ags_r'],
// Glucides.
'nv_carb_g' => $nutri_values['glucides_r'],
// Dont sucres.
'nv_sugar_g' => $nutri_values['sucre_r'],
// Fibres alimentaires.
'nv_fiber_g' => $nutri_values['fib_alim_r'],
// Protéines.
'nv_protein_g' => $nutri_values['proteine_r'],
// Sel.
'nv_salt_g' => $nutri_values['sel_r'],
/** Autres valeurs dispos
* $nutri_values["sodium_r"];
* $nutri_values["ener_e14_r"];
* $nutri_values["fruits_r"];
* $nutri_values["fruits_coque_sech_r"];
* $nutri_values["ener_kjo_r"];
* $nutri_values["fatsat_r"];
* $nutri_values["choval_r"];
* $nutri_values["sugar_r"];
* $nutri_values["fibtg_r"];
* $nutri_values["pro_r"];
* $nutri_values["salteq_r"];
*/
// * Valeurs nutritionnelles par portion.
// Taille portion en grammes.
'nv_serving_size_g' => $nutri_values['taille_port'],
// Energie Kj.
'nv_serving_kj' => $nutri_values['val_energ_kj_p'],
// Energie Kcal.
'nv_serving_kcal' => $nutri_values['val_energ_kcal_p'],
// Graisses.
'nv_serving_fat_g' => $nutri_values['mat_grass_p'],
// Dont acides gras saturés.
'nv_serving_sat_fat_g' => $nutri_values['ags_p'],
// Glucides.
'nv_serving_carb_g' => $nutri_values['glucides_p'],
// Dont sucres.
'nv_serving_sugar_g' => $nutri_values['sucre_p'],
// Fibres alimentaires.
'nv_serving_fiber_g' => $nutri_values['fib_alim_p'],
// Protéines.
'nv_serving_protein_g' => $nutri_values['proteine_p'],
// Sel.
'nv_serving_salt_g' => $nutri_values['sel_p'],
/**
* autres valeurs disponibles
* $nutri_values["sodium_p"];
* $nutri_values["fruits_p"];
*/
// * Apports journaliers.
// Energie % recommandé.
'nv_daily_energy' => $nutri_values['pourcent_apport_ref_kjo_r'],
// Graisses.
'nv_daily_fat' => $nutri_values['pourcent_apport_ref_fat_r'],
// Dont acides gras saturés.
'nv_daily_sat_fat' => $nutri_values['pourcent_apport_ref_fatsat_r'],
// Glucides.
'nv_daily_carb' => $nutri_values['pourcent_apport_ref_choval_r'],
// Dont sucres.
'nv_daily_sugar' => $nutri_values['pourcent_apport_ref_sugar_r'],
// Fibres alimentaires.
'nv_daily_fiber' => $nutri_values['pourcent_apport_ref_fibtg_r'],
// Protéines.
'nv_daily_protein' => $nutri_values['pourcent_apport_ref_pro_r'],
// Sel.
'nv_daily_salt' => $nutri_values['pourcent_apport_ref_salteq_r'],
/**
* Autre valeur dispo
* $nutri_values["pourcent_apport_ref_e14_r"]
*/
// * Nutriscore.
'nutriscore' => $nutri_values['classement'],
'nutriscore_args' => '',
/**
* Infos n'ayant pas de mapping
*
* $nutri_values["val_energ_kj_nut"];
* $nutri_values["val_energ_kcal_nut"];
* $nutri_values["mat_grass_nut"];
* $nutri_values["ags_nut"];
* $nutri_values["glucides_nut"];
* $nutri_values["sucre_nut"];
* $nutri_values["sucre_r"];
* $nutri_values["fib_alim_nut"];
* $nutri_values["proteine_nut"];
* $nutri_values["sel_nut"];
* $nutri_values["apport_ref"];
* $nutri_values["fruits_coque_sech_p"];
* $nutri_values["ener_e14_p"];
* $nutri_values["pourcent_apport_ref_e14_p"];
* $nutri_values["ener_kjo_p"];
* $nutri_values["pourcent_apport_ref_kjo_p"];
* $nutri_values["fat_p"];
* $nutri_values["pourcent_apport_ref_fat_p"];
* $nutri_values["fatsat_p"];
* $nutri_values["pourcent_apport_ref_fatsat_p"];
* $nutri_values["choval_p"];
* $nutri_values["pourcent_apport_ref_choval_p"];
* $nutri_values["sugar_p"];
* $nutri_values["pourcent_apport_ref_sugar_p"];
* $nutri_values["fibtg_p"];
* $nutri_values["pourcent_apport_ref_fibtg_p"];
* $nutri_values["pro_p"];
* $nutri_values["pourcent_apport_ref_pro_p"];
* $nutri_values["salteq_p"];
* $nutri_values["pourcent_apport_ref_salteq_p"];
* $nutri_values["apport_port"];
* $nutri_values["part_p"];
* $nutri_values["part_g"];
* $nutri_values["part_l"];
* $nutri_values["p_sur_l"];
* $nutri_values["pourcent_ags"];
* $nutri_values["pourcent_agi"];
* $nutri_values["pourcent_ss"];
* $nutri_values["pourcent_amidon"];
* $nutri_values["composante_n"];
* $nutri_values["composante_p"];
* $nutri_values["pourcent_fruits"];
* $nutri_values["mat_grass_r_prec"];
* $nutri_values["ags_r_prec"];
* $nutri_values["glucides_r_prec"];
* $nutri_values["sucre_r_prec"];
* $nutri_values["fib_alim_r_prec"];
* $nutri_values["proteine_r_prec"];
* $nutri_values["sel_r_prec"];
* $nutri_values["sodium_r_prec"];
* $nutri_values["fruits_r_prec"];
* $nutri_values["fruits_coque_sech_r_prec"];
* $nutri_values["score_nutri"];
*/
];
}
/**
* Asserts using validation that the name is unique
*
* @param mixed $name
* @param integer|null $id
* @return void
* @throws \Illuminate\Validation\ValidationException If an actu name already exists.
*/
private static function assertNameIsUnique($name, ?int $id = null)
{
$table = (new self())->getTable();
ToolboxHelper::assertFieldIsUnique($table, 'name', $name, $id);
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($name), $id);
}
/**
* Adjusts model status using current status and publish dates
*
* @return void
*/
public function makeStatusSense(): void
{
/** @var \Carbon\Carbon|null */
$publish_at = $this->publish_at;
/** @var \Carbon\Carbon|null */
$unpublish_at = $this->unpublish_at;
// * Ignore unpublish_at in the past
if ($unpublish_at and $unpublish_at->lessThan(now())) {
return;
}
// * Ignore publish_at in the futur
if ($publish_at and $publish_at->greaterThan(now())) {
return;
}
if ($publish_at or $unpublish_at) {
$this->status = $this->planificated_status;
}
}
/**
* Getter the current status following planification dates
*
* @return boolean
*/
public function getPlanificatedStatusAttribute(): bool
{
/** @var \Carbon\Carbon|null */
$publish_at = $this->publish_at;
/** @var \Carbon\Carbon|null */
$unpublish_at = $this->unpublish_at;
if ($publish_at and $unpublish_at) {
return $unpublish_at->greaterThanOrEqualTo(now()) and $publish_at->lessThan(now());
}
if ($publish_at) {
return $publish_at->lessThan(now());
}
if ($unpublish_at) {
return $unpublish_at->greaterThanOrEqualTo(now());
}
return false;
}
/**
* Adjusts model status using current orderable status and orderable dates
*
* @return void
*/
public function makeOrderableSense(): void
{
/** @var \Carbon\Carbon|null */
$orderable_at = $this->orderable_at;
/** @var \Carbon\Carbon|null */
$unorderable_at = $this->unorderable_at;
// * Ignore unorderable_at in the past
if ($unorderable_at and $orderable_at->lessThan(now())) {
return;
}
// * Ignore orderable_at in the futur
if ($orderable_at and $orderable_at->greaterThan(now())) {
return;
}
if ($orderable_at or $unorderable_at) {
$this->status = $this->order_planificated_status;
}
}
/**
* Getter the current orderable status following planification dates
*
* @return boolean
*/
public function getOrderPlanificatedStatusAttribute(): bool
{
/** @var \Carbon\Carbon|null */
$orderable_at = $this->orderable_at;
/** @var \Carbon\Carbon|null */
$unorderable_at = $this->unorderable_at;
if ($orderable_at and $unorderable_at) {
return $unorderable_at->greaterThanOrEqualTo(now()) and $orderable_at->lessThan(now());
}
if ($orderable_at) {
return $orderable_at->lessThan(now());
}
if ($unorderable_at) {
return $unorderable_at->greaterThanOrEqualTo(now());
}
return false;
}
/**
* Assert Is Above Zero
*
* @param string $attribute
* @param string $name
* @param integer $value
* @return void
* @throws \Illuminate\Validation\ValidationException If the value is less or equals to 0.
*/
private static function assertIsAboveZero(string $attribute, string $name, int $value): void
{
Validator::make([
$attribute => $value
], [
$attribute => 'numeric|min:0'
], [], [
$attribute => $name
])->validate();
}
// * MUTATORS
/**
* Setter price
*
* ! Stored as integer (cents)
*
* @param float $value
* @return void
*/
public function setPriceAttribute(float $value)
{
$this->attributes['price'] = \intval($value * 100);
}
/**
* Getter price
*
* ! Stored as integer (cents)
*
* @return float
*/
public function getPriceAttribute(): float
{
return \intval($this->attributes['price']) / 100;
}
// * Dynamic Minicrud inputs
/**
* Fetch Product Types
* @return array<string,string>
* @throws \RuntimeException If the theme does not have a valid 'types' definition.
*/
public static function fetchTypes(): array
{
return collect(\array_replace_recursive(
\collect(DefaultProductType::toArray())->mapWithKeys(function (DefaultProductType $type) {
return [$type->value => (object)$type->jsonSerialize()];
})->all(),
\collect(config('theme.types'))->mapWithKeys(function (array $typeDefinition) {
if (!array_key_exists('type', $typeDefinition)) {
throw new \RuntimeException('Theme type definition must have a type defined');
}
return [$typeDefinition['type']['value'] => (object)$typeDefinition['type']];
})->all()
))->unique()->all();
}
/**
* Fetch Product Types Inputs
* @return array<string,array>
* @throws \RuntimeException If the theme does not have a valid 'types' definition.
*/
public static function fetchTypesInputs(): array
{
$themeTypes = \collect(config('theme.types'))->keyBy('type.value');
return \collect(self::fetchTypes())->mapWithKeys(function (object $type) use ($themeTypes) {
switch (true) {
case $type->value === DefaultProductType::default()->value:
return [$type->value => []];
case $type->value === DefaultProductType::inco()->value:
return [$type->value => DefaultProductType::incoInputs()];
case $themeTypes->has($type->value):
$typeDefinition = $themeTypes->get($type->value);
if (!\is_array($typeDefinition)) {
throw new \RuntimeException('Theme type definition must be an array');
}
if (!array_key_exists('type', $typeDefinition)) {
throw new \RuntimeException('Theme type definition must have a type defined');
}
if (!array_key_exists('data', $typeDefinition)) {
throw new \RuntimeException('Theme type definition must have data defined');
}
return (object)[$typeDefinition['type']['value'] => $typeDefinition['data']];
default:
throw new \RuntimeException("Unhandled default type {$type->value}");
}
})->all();
}
/**
* Get Minicrud data definition for a product type
*
* @param string $typeValue
* @return mixed
* @throws \RuntimeException In the $typeValue does not have a minicrud data definition.
*/
public static function getMinicrudInputsFromType(string $typeValue)
{
if (!($minicrudData = \collect(self::fetchTypesInputs())->get($typeValue))) {
throw new \RuntimeException("Undefined minicrud data definition for type {$typeValue}");
}
return $minicrudData;
}
/**
* Getter discount rate
*
* @return object
* @throws \RuntimeException If the product type is undefined in config.
*/
public function getTypeAttribute(): object
{
$types = self::fetchTypes();
$type = $this->attributes['type'];
if (!\array_key_exists($type, $types)) {
throw new \RuntimeException("Undefined product type '$type'");
}
return $types[$type];
}
/**
* Price calculations
* We need : gross price (prix avant taxe et remises)
* discounted price (prix avant taxe avec remises)
* net price (prix avec taxe)
* net discounted proice (prix avec taxe et remises)
*/
/**
* Getter Utility to bring prices as a pseudo relation
*
* @return array
*/
public function getCalcPrices()
{
return [
'priceHt' => $this->priceHt,
'discountAmount' => $this->discountAmount,
'discountRate' => $this->discountRate,
'discountedPriceHt' => $this->discountedPriceHt,
'priceTtc' => $this->priceTtc,
'discountedPriceTtc' => $this->discountedPriceTtc
];
}
/**
* Getter the gross price
* Accessor method for priceHt
*
* @author Olivier Legat <olivier.legat@ldc.fr>
*
* @return float
*/
public function getPriceHtAttribute()
{
/** Tenir compte de l'unité de facturation et de l'unité de vente (qui doit être stricte)
* si facturation KG et vente piece
* prendre le poids, convertir en KG => pre-requis l'unité de poids doit être un enum
* multiplier le $this->attributes['price'] par les KG
* sinon
* value => value
*/
// FIXME: plus de sell unit management.
if (config('bosettings.sell_unit_management') == "strict") {
// FIXME: invoicing_unit No longer available.
$calculate = ($this->invoicing_unit == InvoicingUnit::kgm()->value
&& $this->sell_unit == SellUnit::pce()->value
);
} else {
$calculate = false;
}
if ($calculate) {
// Weight converter.
$weighted_price = \floatval($this->attributes['price'])
* (\floatval($this->weight) * WeightUnit::convert()[$this->weight_unit]);
return \floatval(round($weighted_price * 1000) / 1000);
} else {
// No calculations, alias of db property.
return \floatval($this->attributes['price']);
}
}
/**
* Getter for discounted price
*
* @return integer
*/
public function getDiscountedPriceHtAttribute(): int
{
return $this->priceHt - $this->discountAmount;
}
/**
* Getter for net price
*
* @return integer
*/
public function getPriceTtcAttribute(): int
{
$priceTtc = $this->priceHt * (1 + ($this->tax_rate->value / 100));
/**
* On travaille avec des centimes puis on redivise par 100 pour avoir des unités monetaires
* on augmente la précision à 3 digits, l'affichage formate les prix sur 2 décimales
*/
return round($priceTtc);
}
/**
* Getter for net discounted price
*
* @return integer
*/
public function getDiscountedPriceTtcAttribute()
{
$discountedPriceTtc = $this->discountedPriceHt * (1 + ($this->tax_rate->value / 100));
// On travaille avec des centimes puis on redivise par 100 pour avoir des unités monetaires.
$roundingDiscountedPriceTTC = \floatval(round($discountedPriceTtc * 1000) / 1000);
// * Show prices for anyone.
return $roundingDiscountedPriceTTC;
}
/**
* Discount calculators
*/
/**
* Getter for discount amount
*
* @return float
*/
public function getDiscountAmountAttribute()
{
/** @var \App\Models\Discount|null */
$applicableDiscount = $this->discounts->first();
if (\is_null($applicableDiscount)) {
return 0;
}
if ($applicableDiscount->type->value == 1) {
$discountAmount = $applicableDiscount->discount_value;
} else {
// On augmente la précision à 3 digits, l'affichage formate les prix sur 2 décimales.
$discountAmount = $this->priceHt * ($applicableDiscount->discount_value / 100);
$discountAmount = round($discountAmount * 1000) / 1000;
}
return \floatval($discountAmount);
}
/**
* Getter discount rate
*
* @return float
*/
public function getDiscountRateAttribute()
{
/** @var \App\Models\Discount|null */
$applicableDiscount = $this->discounts->first();
if (\is_null($applicableDiscount)) {
return 0;
}
if ($applicableDiscount->type->value == 1) {
// On veut un pourcentage.
$discountRate = ($applicableDiscount->discount_value / $this->priceHt) * 100;
} else {
$discountRate = $applicableDiscount->discount_value;
}
return \floatval($discountRate);
}
/**
* buyablePrice attribute alias
*
* ! Overload Gloudemans\Shoppingcart\Contracts\Buyable::getBuyablePrice()
*
* @param mixed $options
* @return float
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function getBuyablePrice($options = null)
{
// @phpcs:enable
return \floatval($this->priceHt);
}
/**
* Config says to display prices ?
* @return boolean
*/
public static function displayPrices(): bool
{
/** @var \App\Models\GeneralConfig */
$config = Cache::get('GeneralConfigGlobal')->where('type', GeneralConfigType::global())->first();
if ($config->data['orders_display_prices_for_auth']['value']) {
return Auth::guard('frontend')->check();
}
return true;
}
/**
* Get displayable price value
*
* @param mixed $value
* @return string
*/
public static function fmtCurrency($value): string
{
return self::displayPrices() ?
sprintf('%s € TTC', number_format(\intval($value) / 100, 2, ',', ' ')) : '-.-- €';
}
}