Resolve "Add in-app support for battery trade-ins" #1700
4 changed files with 153 additions and 30 deletions
|
|
@ -267,6 +267,12 @@ apiv2_account_delete_code_validate:
|
||||||
controller: App\Controller\CustomerAppAPI\AccountController::validateDeleteCode
|
controller: App\Controller\CustomerAppAPI\AccountController::validateDeleteCode
|
||||||
methods: [POST]
|
methods: [POST]
|
||||||
|
|
||||||
|
# trade-in support
|
||||||
|
apiv2_cust_vehicle_trade_in_estimate:
|
||||||
|
path: /apiv2/vehicles/{id}/trade_in_estimate
|
||||||
|
controller: App\Controller\CustomerAppAPI\VehicleController::getTradeInEstimate
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
# insurance
|
# insurance
|
||||||
apiv2_insurance_vehicle_maker_list:
|
apiv2_insurance_vehicle_maker_list:
|
||||||
path: /apiv2/insurance/vehicles/makers
|
path: /apiv2/insurance/vehicles/makers
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ class InvoiceController extends ApiController
|
||||||
'service_type',
|
'service_type',
|
||||||
'cv_id',
|
'cv_id',
|
||||||
// 'batt_id',
|
// 'batt_id',
|
||||||
'trade_in',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!$validity['is_valid']) {
|
if (!$validity['is_valid']) {
|
||||||
|
|
@ -82,22 +81,31 @@ class InvoiceController extends ApiController
|
||||||
|
|
||||||
// check trade-in
|
// check trade-in
|
||||||
// only allow motolite, other, none
|
// only allow motolite, other, none
|
||||||
$trade_in = $req->request->get('trade_in');
|
$trade_in_batt = $req->request->get('trade_in_batt');
|
||||||
switch ($trade_in) {
|
$trade_in_type = $req->request->get('trade_in_type');
|
||||||
|
|
||||||
|
switch ($trade_in_type) {
|
||||||
case TradeInType::MOTOLITE:
|
case TradeInType::MOTOLITE:
|
||||||
case TradeInType::OTHER:
|
case TradeInType::OTHER:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$trade_in = '';
|
$trade_in_type = '';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// right now, the app does not include trade-ins but this might change in the future
|
// add the actual battery item first
|
||||||
if (empty($trade_in))
|
$icrit->addEntry($batt, null, 1);
|
||||||
$icrit->addEntry($batt, null, 1);
|
|
||||||
else
|
// DEBUG
|
||||||
$icrit->addEntry($batt, $trade_in, 1);
|
|
||||||
|
// if we have a trade in, add it as well
|
||||||
|
if (!empty($trade_in_type) && !empty($trade_in_batt)) {
|
||||||
|
$ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt);
|
||||||
|
if (!empty($ti_batt_obj)) {
|
||||||
|
$icrit->addEntry($ti_batt_obj, $trade_in_type, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set if taxable
|
// set if taxable
|
||||||
$icrit->setIsTaxable();
|
$icrit->setIsTaxable();
|
||||||
|
|
|
||||||
|
|
@ -490,7 +490,6 @@ class JobOrderController extends ApiController
|
||||||
$validity = $this->validateRequest($req, [
|
$validity = $this->validateRequest($req, [
|
||||||
'service_type',
|
'service_type',
|
||||||
'cv_id',
|
'cv_id',
|
||||||
'trade_in',
|
|
||||||
'long',
|
'long',
|
||||||
'lat',
|
'lat',
|
||||||
'warranty',
|
'warranty',
|
||||||
|
|
@ -502,7 +501,8 @@ class JobOrderController extends ApiController
|
||||||
}
|
}
|
||||||
|
|
||||||
// trade in type
|
// trade in type
|
||||||
$trade_in = $req->request->get('trade_in');
|
$trade_in_batt = $req->request->get('trade_in_batt');
|
||||||
|
$trade_in_type = $req->request->get('trade_in_type', '');
|
||||||
|
|
||||||
// address
|
// address
|
||||||
$address = $req->request->get('delivery_address', 'Set by mobile application');
|
$address = $req->request->get('delivery_address', 'Set by mobile application');
|
||||||
|
|
@ -580,7 +580,7 @@ class JobOrderController extends ApiController
|
||||||
->setTier1Notes('')
|
->setTier1Notes('')
|
||||||
->setTier2Notes('')
|
->setTier2Notes('')
|
||||||
->setDeliveryAddress($address)
|
->setDeliveryAddress($address)
|
||||||
->setTradeInType($trade_in)
|
->setTradeInType($trade_in_type)
|
||||||
->setDeliveryInstructions($instructions)
|
->setDeliveryInstructions($instructions)
|
||||||
// TODO: error check for valid mode of payment
|
// TODO: error check for valid mode of payment
|
||||||
->setModeOfPayment($req->request->get('mode_of_payment'))
|
->setModeOfPayment($req->request->get('mode_of_payment'))
|
||||||
|
|
@ -669,26 +669,28 @@ class JobOrderController extends ApiController
|
||||||
$icrit->addBattery($batt);
|
$icrit->addBattery($batt);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// NOTE: trade in is currently not supported. Would it be better
|
|
||||||
// if we remove trade-in as a required parameter? Or just leave it be
|
|
||||||
// and simply not process it?
|
|
||||||
// check trade-in
|
// check trade-in
|
||||||
// only allow motolite, other, none
|
// only allow motolite, other, none
|
||||||
switch ($trade_in) {
|
switch ($trade_in_type) {
|
||||||
case TradeInType::MOTOLITE:
|
case TradeInType::MOTOLITE:
|
||||||
case TradeInType::OTHER:
|
case TradeInType::OTHER:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$trade_in = '';
|
$trade_in_type = '';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// right now, the app does not include trade-ins but this might change in the future
|
// add the actual battery item first
|
||||||
if (empty($trade_in))
|
$icrit->addEntry($batt, null, 1);
|
||||||
$icrit->addEntry($batt, null, 1);
|
|
||||||
else
|
// if we have a trade in, add it as well
|
||||||
$icrit->addEntry($batt, $trade_in, 1);
|
if (!empty($trade_in_type) && !empty($trade_in_batt)) {
|
||||||
|
$ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt);
|
||||||
|
if (!empty($ti_batt_obj)) {
|
||||||
|
$icrit->addEntry($ti_batt_obj, $trade_in_type, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set taxable
|
// set taxable
|
||||||
$icrit->setIsTaxable();
|
$icrit->setIsTaxable();
|
||||||
|
|
@ -975,7 +977,6 @@ class JobOrderController extends ApiController
|
||||||
'service_type',
|
'service_type',
|
||||||
'cv_id',
|
'cv_id',
|
||||||
// 'batt_id',
|
// 'batt_id',
|
||||||
'trade_in',
|
|
||||||
'long',
|
'long',
|
||||||
'lat',
|
'lat',
|
||||||
'warranty',
|
'warranty',
|
||||||
|
|
@ -987,7 +988,8 @@ class JobOrderController extends ApiController
|
||||||
}
|
}
|
||||||
|
|
||||||
// trade in type
|
// trade in type
|
||||||
$trade_in = $req->request->get('trade_in');
|
$trade_in_batt = $req->request->get('trade_in_batt');
|
||||||
|
$trade_in_type = $req->request->get('trade_in_type', '');
|
||||||
|
|
||||||
// address
|
// address
|
||||||
$address = $req->request->get('delivery_address', 'Set by mobile application');
|
$address = $req->request->get('delivery_address', 'Set by mobile application');
|
||||||
|
|
@ -1013,7 +1015,7 @@ class JobOrderController extends ApiController
|
||||||
->setTier1Notes('')
|
->setTier1Notes('')
|
||||||
->setTier2Notes('')
|
->setTier2Notes('')
|
||||||
->setDeliveryAddress($address)
|
->setDeliveryAddress($address)
|
||||||
->setTradeInType($trade_in)
|
->setTradeInType($trade_in_type)
|
||||||
->setDeliveryInstructions($instructions)
|
->setDeliveryInstructions($instructions)
|
||||||
// TODO: error check for valid mode of payment
|
// TODO: error check for valid mode of payment
|
||||||
->setModeOfPayment($req->request->get('mode_of_payment'));
|
->setModeOfPayment($req->request->get('mode_of_payment'));
|
||||||
|
|
@ -1098,24 +1100,33 @@ class JobOrderController extends ApiController
|
||||||
|
|
||||||
// check trade-in
|
// check trade-in
|
||||||
// only allow motolite, other, none
|
// only allow motolite, other, none
|
||||||
switch ($trade_in) {
|
switch ($trade_in_type) {
|
||||||
case TradeInType::MOTOLITE:
|
case TradeInType::MOTOLITE:
|
||||||
case TradeInType::OTHER:
|
case TradeInType::OTHER:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$trade_in = '';
|
$trade_in_type = '';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add the actual battery item first
|
||||||
|
$icrit->addEntry($batt, null, 1);
|
||||||
|
|
||||||
|
// if we have a trade in, add it as well
|
||||||
|
if (!empty($trade_in_type) && !empty($trade_in_batt)) {
|
||||||
|
$ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt);
|
||||||
|
if (!empty($ti_batt_obj)) {
|
||||||
|
$icrit->addEntry($ti_batt_obj, $trade_in_type, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set taxable
|
// set taxable
|
||||||
$icrit->setIsTaxable();
|
$icrit->setIsTaxable();
|
||||||
|
|
||||||
// set JO source
|
// set JO source
|
||||||
$icrit->setSource(TransactionOrigin::MOBILE_APP);
|
$icrit->setSource(TransactionOrigin::MOBILE_APP);
|
||||||
|
|
||||||
$icrit->addEntry($batt, $trade_in, 1);
|
|
||||||
|
|
||||||
// send to invoice generator
|
// send to invoice generator
|
||||||
$invoice = $ic->generateInvoice($icrit);
|
$invoice = $ic->generateInvoice($icrit);
|
||||||
$jo->setInvoice($invoice);
|
$jo->setInvoice($invoice);
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,12 @@ use Symfony\Component\HttpFoundation\Request;
|
||||||
use Catalyst\ApiBundle\Component\Response as ApiResponse;
|
use Catalyst\ApiBundle\Component\Response as ApiResponse;
|
||||||
|
|
||||||
use App\Entity\CustomerVehicle;
|
use App\Entity\CustomerVehicle;
|
||||||
|
use App\Entity\JobOrder;
|
||||||
use App\Entity\VehicleManufacturer;
|
use App\Entity\VehicleManufacturer;
|
||||||
use App\Entity\Vehicle;
|
use App\Entity\Vehicle;
|
||||||
|
use App\Ramcar\JOStatus;
|
||||||
|
use App\Ramcar\ServiceType;
|
||||||
|
use App\Ramcar\TradeInType;
|
||||||
use App\Ramcar\InsuranceApplicationStatus;
|
use App\Ramcar\InsuranceApplicationStatus;
|
||||||
use App\Service\PayMongoConnector;
|
use App\Service\PayMongoConnector;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
|
@ -170,6 +174,39 @@ class VehicleController extends ApiController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTradeInEstimate(Request $req, $id)
|
||||||
|
{
|
||||||
|
// check requirements
|
||||||
|
$validity = $this->validateRequest($req);
|
||||||
|
|
||||||
|
if (!$validity['is_valid']) {
|
||||||
|
return new ApiResponse(false, $validity['error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get customer vehicle
|
||||||
|
$cv = $this->em->getRepository(CustomerVehicle::class)->find($id);
|
||||||
|
|
||||||
|
// check if it exists
|
||||||
|
if ($cv == null) {
|
||||||
|
return new ApiResponse(false, 'Vehicle does not exist.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it's owned by customer
|
||||||
|
if ($cv->getCustomer()->getID() != $this->session->getCustomer()->getID()) {
|
||||||
|
return new ApiResponse(false, 'Invalid vehicle.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// check trade in value
|
||||||
|
$result = $this->getTIEstimateByCV($cv);
|
||||||
|
|
||||||
|
// response
|
||||||
|
return new ApiResponse(true, '', [
|
||||||
|
'trade_in_batt' => $result['trade_in_batt'],
|
||||||
|
'trade_in_type' => $result['trade_in_type'],
|
||||||
|
'trade_in_value' => $result['trade_in_value'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function listVehicles(Request $req, PayMongoConnector $paymongo)
|
public function listVehicles(Request $req, PayMongoConnector $paymongo)
|
||||||
{
|
{
|
||||||
// validate params
|
// validate params
|
||||||
|
|
@ -284,6 +321,67 @@ class VehicleController extends ApiController
|
||||||
return new ApiResponse();
|
return new ApiResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getTIEstimateByCV($cv)
|
||||||
|
{
|
||||||
|
// compute for trade in value
|
||||||
|
$trade_in_batt = null;
|
||||||
|
$trade_in_value = 0;
|
||||||
|
$trade_in_type = TradeInType::OTHER;
|
||||||
|
$previous_jo_found = false;
|
||||||
|
|
||||||
|
// check for last battery replacement JO
|
||||||
|
$last_jo = $this->em->getRepository(JobOrder::class)->findOneBy([
|
||||||
|
'service_type' => [
|
||||||
|
ServiceType::BATTERY_REPLACEMENT_NEW, ServiceType::BATTERY_REPLACEMENT_WARRANTY
|
||||||
|
],
|
||||||
|
'status' => JOStatus::FULFILLED,
|
||||||
|
'cus_vehicle' => $cv,
|
||||||
|
], ['date_create' => 'desc']);
|
||||||
|
|
||||||
|
if (!empty($last_jo)) {
|
||||||
|
$items = $last_jo->getInvoice()->getItems();
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
// find the first battery item and get its trade-in value
|
||||||
|
$item_battery = $item->getBattery();
|
||||||
|
if (!empty($item_battery)) {
|
||||||
|
$trade_in_type = TradeInType::MOTOLITE;
|
||||||
|
$trade_in_batt = $item_battery->getID();
|
||||||
|
$previous_jo_found = true;
|
||||||
|
$size = $item_battery->getSize();
|
||||||
|
$trade_in_value = $size->getTIPriceMotolite();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: possibly refactor this bit
|
||||||
|
// if no valid previous JO is found, base the trade-in value on recommended batteries
|
||||||
|
if (!$previous_jo_found) {
|
||||||
|
$comp_batteries = $cv->getVehicle()->getBatteries();
|
||||||
|
|
||||||
|
// get the lowest trade-in value from the list of batteries
|
||||||
|
if (!empty($comp_batteries)) {
|
||||||
|
foreach ($comp_batteries as $battery) {
|
||||||
|
$size = $battery->getSize();
|
||||||
|
$size_ti = $size->getTIPriceOther();
|
||||||
|
|
||||||
|
// get the lowest value or set if not set yet
|
||||||
|
if ($size_ti < $trade_in_value || $trade_in_value == 0) {
|
||||||
|
$trade_in_value = $size_ti;
|
||||||
|
$trade_in_batt = $battery->getID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'trade_in_batt' => $trade_in_batt,
|
||||||
|
'trade_in_type' => $trade_in_type,
|
||||||
|
'trade_in_value' => $trade_in_value,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
protected function generateVehicleInfo(CustomerVehicle $cv, $include_insurance = false, PayMongoConnector $paymongo)
|
protected function generateVehicleInfo(CustomerVehicle $cv, $include_insurance = false, PayMongoConnector $paymongo)
|
||||||
{
|
{
|
||||||
$battery_id = null;
|
$battery_id = null;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue