Updates to Product Model, product updates, enable pricing update, improved formating of product services

This commit is contained in:
2023-05-04 22:17:42 +10:00
parent 95bb55aad8
commit 0f91ce4940
21 changed files with 491 additions and 138 deletions

View File

@@ -11,7 +11,6 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Leenooks\Traits\ScopeActive;
use App\Interfaces\{IDs,ProductItem};
@@ -24,12 +23,15 @@ use App\Traits\{ProductDetails,SiteID};
* Products have one Type (Product/*), made of an Offering (Supplier/*) from a Supplier.
* Conversely, Suppliers provide Offerings (Supplier/*) which belong to a Type (Product/*) of a Product.
*
* So each product attribute has:
* + supplied : Supplier product provided for this offering (Supplier/*)
* + type : Returns the underlying product object, representing the type of product (Product/*)
*
* Attributes for products:
* + lid : Local ID for product (part number)
* + sid : System ID for product (part number)
* + category : Type of product supplied
* + category_name : Type of product supplied (Friendly Name for display, not for internal logic)
* + supplied : Supplier product provided for this offering
* + supplier : Supplier for this offering
* + name : Brief Name for our product with name_detail
* + name_short : Product ID for our Product (description.name => name_short)
@@ -41,9 +43,8 @@ use App\Traits\{ProductDetails,SiteID};
* + setup_charge_taxable : Charge to setup this product including taxes
* + base_charge : Default billing amount
* + base_charge_taxable : Default billing amount including taxes
* + min_charge : Minimum cost taking into account billing interval and setup costs
* + min_charge_taxable : Minimum cost taking into account billing interval and setup costs including taxes
* + type : Returns the underlying product object, representing the type of product
* + min_charge : Minimum charge taking into account billing interval and setup charges
* + min_charge_taxable : Minimum charge taking into account billing interval and setup charges including taxes
*
* Attributes for product types (type - Product/*)
* + name : Short Name for our Product
@@ -74,7 +75,37 @@ class Product extends Model implements IDs
'pricing'=>'collection',
];
protected $with = ['translate'];
/* STATIC */
/**
* Return a list of available product types
*
* @return Collection
*/
public static function availableTypes(): Collection
{
$models = collect(File::allFiles(app_path()))
->map(function ($item) {
$path = $item->getRelativePathName();
$class = sprintf('%s%s',
Container::getInstance()->getNamespace(),
strtr(substr($path, 0, strrpos($path, '.')), '/', '\\'));
return $class;
})
->filter(function ($class) {
$valid = FALSE;
if (class_exists($class)) {
$reflection = new \ReflectionClass($class);
$valid = $reflection->isSubclassOf(ProductItem::class) && (! $reflection->isAbstract());
}
return $valid;
});
return $models->values();
}
/* RELATIONS */
@@ -349,33 +380,38 @@ class Product extends Model implements IDs
/* METHODS */
/**
* Return a list of available product types
* Return the charge from the pricing table for the specific time period and group
*
* @return Collection
* @param int $timeperiod
* @param Group $go
* @param string $type
* @return float|null
*/
function availableTypes(): Collection
public function charge(int $timeperiod,Group $go,string $type): ?float
{
$models = collect(File::allFiles(app_path()))
->map(function ($item) {
$path = $item->getRelativePathName();
$class = sprintf('%s%s',
Container::getInstance()->getNamespace(),
strtr(substr($path, 0, strrpos($path, '.')), '/', '\\'));
return Arr::get($this->pricing,sprintf('%d.%d.%s',$timeperiod,$go->id,$type));
}
return $class;
})
->filter(function ($class) {
$valid = FALSE;
/**
* Do we have a charge for specific group/period
*
* @param int $timeperiod
* @return bool
*/
public function charge_available(int $timeperiod): bool
{
return Arr::get($this->pricing,sprintf('%d.show',$timeperiod),FALSE);
}
if (class_exists($class)) {
$reflection = new \ReflectionClass($class);
$valid = $reflection->isSubclassOf(ProductItem::class) && (! $reflection->isAbstract());
}
return $valid;
});
return $models->values();
/**
* Return a normalize price dependent on the product, ie: Broadband = Monthly, Domain = Yearly, etc
*
* @note: By definition products are normalised, as their cost price is based on the default billing interval
* @return float
*/
public function cost_normalized(): float
{
return number_format(Tax::tax_calc($this->supplied->base_cost,config('site')->taxes),2);
}
/**
@@ -385,6 +421,7 @@ class Product extends Model implements IDs
* @param int|NULL $timeperiod
* @param Group|NULL $go
* @return float
* @todo use self::charge()
*/
private function getCharge(string $type,int $timeperiod=NULL,Group $go=NULL): float
{
@@ -407,16 +444,9 @@ class Product extends Model implements IDs
$alt_tp--;
}
if (! is_null($price) && $alt_tp !== $timeperiod) {
// If we havent got a price, we'll extrapolate one, except for setup charges
if (! is_null($price) && ($alt_tp !== $timeperiod) && ($type !== 'setup'))
$price = $price*Invoice::billing_change($alt_tp,$timeperiod);
}
}
// @todo - if price doesnt exist for the time period, reduce down to timeperiod 1 and multiply appropriately.
if (is_null($price)) {
Log::error(sprintf('Price is still null for [%d] timeperiod [%d] group [%d]',$this->id,$timeperiod,$go->id));
$price = 0;
}
return round($price,2);