Modify invoice engine. #744
This commit is contained in:
parent
1c02138f52
commit
0bb32f47b5
7 changed files with 209 additions and 261 deletions
26
src/Invoice/ServiceType.php
Normal file
26
src/Invoice/ServiceType.php
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Invoice;
|
||||||
|
|
||||||
|
use App\InvoiceInterface;
|
||||||
|
|
||||||
|
class ServiceType implements InvoiceInterface
|
||||||
|
{
|
||||||
|
public function check($settings, $criteria)
|
||||||
|
{
|
||||||
|
// compare specified type ids with service type of criteria
|
||||||
|
$stypes = $settings['type_id'];
|
||||||
|
foreach ($stypes as $type_id)
|
||||||
|
{
|
||||||
|
if ($type_id == $criteria->getServiceType())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTemplate()
|
||||||
|
{
|
||||||
|
return 'invoice/service_type.html.twig';
|
||||||
|
}
|
||||||
|
}
|
||||||
169
src/InvoiceCriteria.php
Normal file
169
src/InvoiceCriteria.php
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ramcar;
|
||||||
|
|
||||||
|
use App\Entity\Battery;
|
||||||
|
use App\Entity\Promo;
|
||||||
|
use App\Entity\CustomerVehicle;
|
||||||
|
use App\Entity\ServiceCharge;
|
||||||
|
|
||||||
|
class InvoiceCriteria
|
||||||
|
{
|
||||||
|
protected $stype;
|
||||||
|
protected $promos;
|
||||||
|
protected $cv;
|
||||||
|
protected $flag_coolant;
|
||||||
|
protected $discount;
|
||||||
|
protected $service_charges;
|
||||||
|
protected $flag_taxable;
|
||||||
|
|
||||||
|
// entries are battery and trade-in combos
|
||||||
|
protected $entries;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->stype = 0;
|
||||||
|
$this->promos = [];
|
||||||
|
$this->entries = [];
|
||||||
|
$this->cv = null;
|
||||||
|
$this->flag_coolant = false;
|
||||||
|
$this->discount = 0;
|
||||||
|
$this->service_charges = [];
|
||||||
|
$this->flag_taxable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setServiceType($stype)
|
||||||
|
{
|
||||||
|
// TODO: validate service type
|
||||||
|
$this->stype = $stype;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServiceType()
|
||||||
|
{
|
||||||
|
return $this->stype;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function addBattery(Battery $battery, $qty = 1)
|
||||||
|
{
|
||||||
|
for ($i = 0; $i < $qty; $i++)
|
||||||
|
$this->batteries[] = $battery;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBatteries()
|
||||||
|
{
|
||||||
|
return $this->batteries;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function addPromo(Promo $promo)
|
||||||
|
{
|
||||||
|
$this->promos[] = $promo;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPromos()
|
||||||
|
{
|
||||||
|
return $this->promos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function addTradeIn($is_motolite, $qty = 1)
|
||||||
|
{
|
||||||
|
// NOTE: this asumes that all the rates for trade-ins are standardized
|
||||||
|
// for motolite and non-motolite trade-ins
|
||||||
|
|
||||||
|
for ($i = 0; $i < $qty; $i++)
|
||||||
|
{
|
||||||
|
if ($is_motolite)
|
||||||
|
$trade_in = 'motolite';
|
||||||
|
else
|
||||||
|
$trade_in = 'other';
|
||||||
|
|
||||||
|
$this->trade_ins[] = $trade_in;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTradeIns()
|
||||||
|
{
|
||||||
|
return $this->trade_ins;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function addEntry($battery, $trade_in, $qty)
|
||||||
|
{
|
||||||
|
// trade_in is null if no trade_in specified
|
||||||
|
|
||||||
|
$entry = [
|
||||||
|
'battery' => $battery,
|
||||||
|
'trade_in' => $trade_in,
|
||||||
|
'qty' => $qty
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->entries[] = $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEntries()
|
||||||
|
{
|
||||||
|
return $this->entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCustomerVehicle(CustomerVehicle $cv = null)
|
||||||
|
{
|
||||||
|
$this->cv = $cv;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCustomerVehicle()
|
||||||
|
{
|
||||||
|
return $this->cv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setHasCoolant($flag = true)
|
||||||
|
{
|
||||||
|
$this->flag_coolant = $flag;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasCoolant()
|
||||||
|
{
|
||||||
|
return $this->flag_coolant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDiscount($discount)
|
||||||
|
{
|
||||||
|
$this->discount = $discount;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDiscount()
|
||||||
|
{
|
||||||
|
return $this->discount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addServiceCharge(ServiceCharge $service_charge)
|
||||||
|
{
|
||||||
|
$this->service_charges[] = $service_charge;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServiceCharges()
|
||||||
|
{
|
||||||
|
return $this->service_charges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsTaxable($flag = true)
|
||||||
|
{
|
||||||
|
$this->flag_taxable = $flag;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isTaxable()
|
||||||
|
{
|
||||||
|
return $this->flag_taxable;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
12
src/InvoiceInterface.php
Normal file
12
src/InvoiceInterface.php
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
interface InvoiceInterface
|
||||||
|
{
|
||||||
|
// check if the invoice rule aka settings is in the criteria
|
||||||
|
public function check($settings, $criteria);
|
||||||
|
|
||||||
|
// display the html partial for the form
|
||||||
|
public function getTemplate();
|
||||||
|
}
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Service;
|
|
||||||
|
|
||||||
class InvoiceBatteryManager
|
|
||||||
{
|
|
||||||
public function processBatteries($con_batts, &$total)
|
|
||||||
{
|
|
||||||
// process batteries
|
|
||||||
$invoice_batteries = [];
|
|
||||||
foreach ($con_batts as $con_data)
|
|
||||||
{
|
|
||||||
// get battery
|
|
||||||
$battery = $con_data['battery'];
|
|
||||||
|
|
||||||
// get the quantity to purchase
|
|
||||||
$qty = $con_data['qty'];
|
|
||||||
|
|
||||||
$sell_price = $batt->getSellingPrice();
|
|
||||||
|
|
||||||
$computed_price = bcmul($sell_price, $qty, 2);
|
|
||||||
$invoice_batteries = [
|
|
||||||
'title' => $battery->getModel()->getName() . ' ' . $battery->getSize()->getName(),
|
|
||||||
'battery' => $battery,
|
|
||||||
'qty' => $qty,
|
|
||||||
'sell_price' => $computed_price,
|
|
||||||
'vat' => 0.0,
|
|
||||||
'vat_ex_price' => 0.0,
|
|
||||||
];
|
|
||||||
|
|
||||||
$total['sell_price'] = bcadd($total['sell_price'], $computed_price, 2);
|
|
||||||
$total['vat'] += 0.00;
|
|
||||||
$total['vat_ex_price'] += 0.00;
|
|
||||||
|
|
||||||
$total['total_price'] = bcadd($total['total_price'], $computed_price, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $invoice_batteries;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -17,143 +17,6 @@ use App\Entity\Battery;
|
||||||
|
|
||||||
class InvoiceManager
|
class InvoiceManager
|
||||||
{
|
{
|
||||||
protected $security;
|
// TODO: get list of invoice rules from .env
|
||||||
protected $em;
|
// TODO: get the service type fees
|
||||||
protected $validator;
|
|
||||||
protected $inv_batt;
|
|
||||||
protected $inv_trade;
|
|
||||||
protected $inv_tax;
|
|
||||||
|
|
||||||
public function __construct(Security $service, EntityManagerInterface $em, ValidatorInterface $validator,
|
|
||||||
InvoiceBatteryManager $inv_batt, InvoiceTradeInManager $inv_trade, InvoiceTaxManager $inv_tax)
|
|
||||||
{
|
|
||||||
$this->security = $security;
|
|
||||||
$this->em = $em;
|
|
||||||
$this->validator = $validator;
|
|
||||||
$this->inv_batt = $inv_batt;
|
|
||||||
$this->inv_trade = $inv_trade;
|
|
||||||
$this->inv_tax = $inv_tax;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateInvoice(InvoiceCriteria $criteria)
|
|
||||||
{
|
|
||||||
// initialize
|
|
||||||
$invoice = new Invoice();
|
|
||||||
$total = [
|
|
||||||
'sell_price' => 0.0,
|
|
||||||
'vat' => 0.0,
|
|
||||||
'vat_ex_price' => 0.0,
|
|
||||||
'ti_rate' => 0.0,
|
|
||||||
'total_price' => 0.0,
|
|
||||||
'discount' => 0.0,
|
|
||||||
];
|
|
||||||
|
|
||||||
$stype = $criteria->getServiceType();
|
|
||||||
$cv = $criteria->getCustomerVehicle();
|
|
||||||
$has_coolant = $criteria->hasCoolant();
|
|
||||||
|
|
||||||
switch($stype)
|
|
||||||
{
|
|
||||||
case ServiceType::JUMPSTART_TROUBLESHOOT:
|
|
||||||
$this->processJumpstart($total, $invoice);
|
|
||||||
break;
|
|
||||||
case ServiceType::JUMPSTART_WARRANTY:
|
|
||||||
$this->processJumpstartWarranty($total, $invoice);
|
|
||||||
break;
|
|
||||||
case ServiceType::BATTERY_REPLACEMENT_NEW:
|
|
||||||
$this->processEntries($total, $criteria, $invoice);
|
|
||||||
$this->processDiscount($total, $criteria, $invoice, $cust_tag_info);
|
|
||||||
break;
|
|
||||||
case ServiceType::BATTERY_REPLACEMENT_WARRANTY:
|
|
||||||
$this->processWarranty($total, $criteria, $invoice);
|
|
||||||
break;
|
|
||||||
case ServiceType::POST_RECHARGED:
|
|
||||||
$this->processRecharge($total, $invoice);
|
|
||||||
break;
|
|
||||||
case ServiceType::POST_REPLACEMENT:
|
|
||||||
$this->processReplacement($total, $invoice);
|
|
||||||
break;
|
|
||||||
case ServiceType::TIRE_REPAIR:
|
|
||||||
$this->processTireRepair($total, $invoice, $cv);
|
|
||||||
break;
|
|
||||||
case ServiceType::OVERHEAT_ASSISTANCE:
|
|
||||||
$this->processOverheat($total, $invoice, $cv, $has_coolant);
|
|
||||||
break;
|
|
||||||
case ServiceType::EMERGENCY_REFUEL:
|
|
||||||
$ftype = $criteria->getCustomerVehicle()->getFuelType();
|
|
||||||
$this->processRefuel($total, $invoice, $cv);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// process promos here?
|
|
||||||
|
|
||||||
// get current user
|
|
||||||
$user = $this->security->getUser();
|
|
||||||
// check if user is User or APIUser
|
|
||||||
if ($user instanceof User)
|
|
||||||
{
|
|
||||||
$invoice->setCreatedBy($user);
|
|
||||||
}
|
|
||||||
|
|
||||||
$invoice->setTotalPrice($total['total_price'])
|
|
||||||
->setVATExclusivePrice($total['vat_ex_price'])
|
|
||||||
->setVAT($total['vat'])
|
|
||||||
->setDiscount($total['discount'])
|
|
||||||
->setTradeIn($total['ti_rate'])
|
|
||||||
->setStatus(InvoiceStatus::DRAFT);
|
|
||||||
|
|
||||||
return $invoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
// process battery sales and trade-ins
|
|
||||||
protected function processEntries(&$total, InvoiceCriteria $criteria, Invoice $invoice)
|
|
||||||
{
|
|
||||||
$entries = $criteria->getEntries();
|
|
||||||
|
|
||||||
$con_batts = [];
|
|
||||||
$con_tis = [];
|
|
||||||
foreach($entries as $entry)
|
|
||||||
{
|
|
||||||
// batteries purchased should have trade_in set to null
|
|
||||||
$batt = $entry['battery'];
|
|
||||||
$qty = $entry['qty'];
|
|
||||||
$trade_in = $entry['trade_in'];
|
|
||||||
$size = $batt->getSize();
|
|
||||||
|
|
||||||
// consolidate batteries
|
|
||||||
$batt_id = $batt->getID();
|
|
||||||
if (!isset($con_batts[$batt_id]))
|
|
||||||
$con_batts[$batt->getID()] = [
|
|
||||||
'batt' => $batt,
|
|
||||||
'qty' => 0
|
|
||||||
];
|
|
||||||
$con_batts[$batt_id]['qty']++;
|
|
||||||
|
|
||||||
// no trade-in
|
|
||||||
if ($trade_in == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// consolidate trade-ins
|
|
||||||
// need to set battery for trade in so we know what battery is for trade in
|
|
||||||
// possible that battery purchased is not the same battery for trade in
|
|
||||||
$ti_key = $size->getID() . '|' . $trade_in;
|
|
||||||
if (!isset($con_tis[$ti_key]))
|
|
||||||
$con_tis[$ti_key] = [
|
|
||||||
'batt' => $batt,
|
|
||||||
'size' => $size,
|
|
||||||
'trade_in' => $trade_in,
|
|
||||||
'qty' => 0
|
|
||||||
];
|
|
||||||
$con_tis[$ti_key]['qty']++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$inv_batt_entries = $this->inv_batt->processBatteries($con_batts, $total);
|
|
||||||
$inv_trade_in_entries = $this->inv_trade->processTradeIns($con_tis, $total);
|
|
||||||
|
|
||||||
// check if we need to compute tax
|
|
||||||
if ($criteria->isTaxable())
|
|
||||||
{
|
|
||||||
$this->inv_tax->computeTax($inv_batt_entries, $total);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Service;
|
|
||||||
|
|
||||||
class InvoiceTaxManager
|
|
||||||
{
|
|
||||||
public function computeTax($inv_batt_entries, &$total)
|
|
||||||
{
|
|
||||||
foreach ($inv_batt_entries as $batt_entry)
|
|
||||||
{
|
|
||||||
$vat = $this->getTaxAmount($sell_price);
|
|
||||||
$vat_ex_price = $this->getTaxExclusivePrice($sell_price);
|
|
||||||
|
|
||||||
// TODO: add to the total array the computed vat prices
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: use bc functions for these
|
|
||||||
protected function getTaxAmount($price)
|
|
||||||
{
|
|
||||||
$vat_ex_price = $this->getTaxExclusivePrice($price);
|
|
||||||
return $price - $vat_ex_price;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getTaxExclusivePrice($price)
|
|
||||||
{
|
|
||||||
return round($price / (1 + self::TAX_RATE), 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Service;
|
|
||||||
|
|
||||||
use App\Ramcar\TradeInType;
|
|
||||||
|
|
||||||
class InvoiceTradeInManager
|
|
||||||
{
|
|
||||||
public function processTradeIns($con_tis, &$total)
|
|
||||||
{
|
|
||||||
$invoice_trade_ins = [];
|
|
||||||
foreach($con_tis as $ti)
|
|
||||||
{
|
|
||||||
$qty = $ti['qty'];
|
|
||||||
$ti_rate = $this->getTradeInRate($ti);
|
|
||||||
|
|
||||||
$ti_price = bcmul($ti_rate, -1, 2);
|
|
||||||
$invoice_trade_ins = [
|
|
||||||
'title' => 'Trade-in ' . TradeInType::getName($ti['trade_in']) . ' ' . $ti['size']->getName() . ' battery'),
|
|
||||||
'qty' => $qty,
|
|
||||||
'price' => $ti_price,
|
|
||||||
];
|
|
||||||
|
|
||||||
$tti_price = bcmul($ti_rate, $qty, 2);
|
|
||||||
|
|
||||||
$total['ti_rate'] = bcadd($total['ti_rate'], $tti_price, 2);
|
|
||||||
$total['total_price'] = bcsub($total['total_price'], $tti_price, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $invoice_trade_ins;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getTradeInRate($ti)
|
|
||||||
{
|
|
||||||
$size = $ti['size'];
|
|
||||||
$trade_in = $ti['trade_in'];
|
|
||||||
|
|
||||||
if ($trade_in == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
switch ($trade_in)
|
|
||||||
{
|
|
||||||
case TradeInType::MOTOLITE:
|
|
||||||
return $size->getTIPriceMotolite();
|
|
||||||
case TradeInType::PREMIUM:
|
|
||||||
return $size->getTIPricePremium();
|
|
||||||
case TradeInType::OTHER:
|
|
||||||
return $size->getTIPriceOther();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue