diff --git a/config/services.yaml b/config/services.yaml index 8c8024f5..71d13a98 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -130,3 +130,15 @@ services: $menu_name: "main_menu" tags: - { name: kernel.event_listener, event: kernel.controller, method: onKernelController } + + # invoice generator + App\Service\InvoiceGenerator\CMBInvoiceGenerator: ~ + + # invoice generator interface + App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\CMBInvoiceGenerator" + + # job order generator + App\Service\JobOrderGenerator\CMBJobOrderGenerator: ~ + + #job order generator interface + App\Service\JobOrderGeneratorInterface: "@App\\Service\\JobOrderGenerator\\CMBJobOrderGenerator" diff --git a/src/Controller/APIController.php b/src/Controller/APIController.php index 075879bb..5642dad7 100644 --- a/src/Controller/APIController.php +++ b/src/Controller/APIController.php @@ -24,7 +24,7 @@ use App\Ramcar\TransactionOrigin; use App\Ramcar\TradeInType; use App\Ramcar\JOEventType; -use App\Service\InvoiceCreator; +use App\Service\InvoiceGeneratorInterface; use App\Service\RisingTideGateway; use App\Service\MQTTClient; use App\Service\GeofenceTracker; @@ -808,7 +808,7 @@ class APIController extends Controller return $res->getReturnResponse(); } - public function requestJobOrder(Request $req, InvoiceCreator $ic, GeofenceTracker $geo) + public function requestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo) { // check required parameters and api key $required_params = [ @@ -973,7 +973,7 @@ class APIController extends Controller $icrit->addEntry($batt, $trade_in, 1); // send to invoice generator - $invoice = $ic->processCriteria($icrit); + $invoice = $ic->generateInvoice($icrit); $jo->setInvoice($invoice); $em->persist($jo); @@ -1020,7 +1020,7 @@ class APIController extends Controller return $res->getReturnResponse(); } - public function getEstimate(Request $req, InvoiceCreator $ic) + public function getEstimate(Request $req, InvoiceGeneratorInterface $ic) { // $this->debugRequest($req); @@ -1120,7 +1120,7 @@ class APIController extends Controller $icrit->addEntry($batt, $trade_in, 1); // send to invoice generator - $invoice = $ic->processCriteria($icrit); + $invoice = $ic->generateInvoice($icrit); // make invoice json data $data = [ diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index 8e9c7d46..4f5bfd76 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -8,7 +8,6 @@ use App\Ramcar\WarrantyClass; use App\Ramcar\DiscountApply; use App\Ramcar\TradeInType; use App\Ramcar\InvoiceCriteria; -use App\Ramcar\InvoiceStatus; use App\Ramcar\ModeOfPayment; use App\Ramcar\TransactionOrigin; use App\Ramcar\JOEventType; @@ -27,7 +26,8 @@ use App\Entity\Battery; use App\Entity\JOEvent; use App\Entity\JORejection; -use App\Service\InvoiceCreator; +use App\Service\InvoiceGeneratorInterface; +use App\Service\JobOrderGeneratorInterface; use App\Service\MapTools; use App\Service\HubCounter; use App\Service\MQTTClient; @@ -251,7 +251,7 @@ class JobOrderController extends Controller return $this->render('job-order/form.html.twig', $params); } - public function openEditSubmit(Request $req, ValidatorInterface $validator, InvoiceCreator $ic, $id) + public function openEditSubmit(Request $req, ValidatorInterface $validator, JobOrderGeneratorInterface $joc, $id) { $this->denyAccessUnlessGranted('jo_open.edit', null, 'No access.'); @@ -297,69 +297,11 @@ class JobOrderController extends Controller // did they change invoice? $invoice_items = $req->request->get('invoice_items', []); $invoice_change = $req->request->get('invoice_change', 0); - if ($invoice_change) - { - // instantiate invoice criteria - $criteria = new InvoiceCriteria(); - $criteria->setServiceType($stype) - ->setCustomerVehicle($obj->getCustomerVehicle()); + $promo_id = $req->request->get('invoice_promo'); + + // call service to generate job order and invoice + $joc->generateJobOrder($obj, $promo_id, $invoice_change, $invoice_items, $error_array); - $ierror = $this->invoicePromo($em, $criteria, $req->request->get('invoice_promo')); - - // check for trade in so we can mark it for mobile app - foreach ($invoice_items as $item) - { - // get first trade-in - if (!empty($item['trade_in'])) - { - $obj->setTradeInType($item['trade_in']); - break; - } - } - - if (!$ierror) - $ierror = $this->invoiceBatteries($em, $criteria, $invoice_items); - - if ($ierror) - { - $error_array['invoice'] = $ierror; - } - else - { - // generate the invoice - $iobj = $ic->processCriteria($criteria); - $iobj->setStatus(InvoiceStatus::DRAFT) - ->setCreatedBy($this->getUser()); - - // validate - $ierrors = $validator->validate($iobj); - - // add errors to list - foreach ($ierrors as $error) { - $error_array[$error->getPropertyPath()] = $error->getMessage(); - } - - // remove previous invoice - $old_invoice = $obj->getInvoice(); - $em->remove($old_invoice); - $em->flush(); - - // add invoice to JO - $obj->setInvoice($iobj); - - // persist invoice - $em->persist($iobj); - } - } - - - // validate - $errors = $validator->validate($obj); - - // add errors to list - foreach ($errors as $error) { - $error_array[$error->getPropertyPath()] = $error->getMessage(); - } } // check if any errors were found @@ -371,17 +313,6 @@ class JobOrderController extends Controller ], 422); } - // the event - $event = new JOEvent(); - $event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::OPEN_EDIT) - ->setUser($this->getUser()) - ->setJobOrder($obj); - $em->persist($event); - - // validated! save the entity - $em->flush(); - // return successful response return $this->json([ 'success' => 'Changes have been saved!' @@ -425,7 +356,8 @@ class JobOrderController extends Controller return $this->render('job-order/form.html.twig', $params); } - public function incomingSubmit(Request $req, ValidatorInterface $validator, InvoiceCreator $ic) + public function incomingSubmit(Request $req, ValidatorInterface $validator, + JobOrderGeneratorInterface $joc) { $this->denyAccessUnlessGranted('jo_in.list', null, 'No access.'); @@ -491,63 +423,12 @@ class JobOrderController extends Controller } } - // instantiate invoice criteria - $criteria = new InvoiceCriteria(); - $criteria->setServiceType($stype) - ->setCustomerVehicle($cust_vehicle); + // call service to generate job order and invoice + $invoice_items = $req->request->get('invoice_items', []); + $promo_id = $req->request->get('invoice_promo'); + $invoice_change = true; - $ierror = $this->invoicePromo($em, $criteria, $req->request->get('invoice_promo')); - $invoice_items = $req->request->get('invoice_items'); - - if (!$ierror && !empty($invoice_items)) - { - // check for trade in so we can mark it for mobile app - foreach ($invoice_items as $item) - { - // get first trade-in - if (!empty($item['trade_in'])) - { - $obj->setTradeInType($item['trade_in']); - break; - } - } - - $ierror = $this->invoiceBatteries($em, $criteria, $invoice_items); - } - - if ($ierror) - { - $error_array['invoice'] = $ierror; - } - else - { - // generate the invoice - $iobj = $ic->processCriteria($criteria); - $iobj->setStatus(InvoiceStatus::DRAFT) - ->setCreatedBy($this->getUser()); - - // validate - $ierrors = $validator->validate($iobj); - - // add errors to list - foreach ($ierrors as $error) { - $error_array[$error->getPropertyPath()] = $error->getMessage(); - } - - // add invoice to JO - $obj->setInvoice($iobj); - - // save - $em->persist($iobj); - } - - // validate - $errors = $validator->validate($obj); - - // add errors to list - foreach ($errors as $error) { - $error_array[$error->getPropertyPath()] = $error->getMessage(); - } + $joc->generateJobOrder($obj, $promo_id, $invoice_change, $invoice_items, $error_array); } // check if any errors were found @@ -559,19 +440,6 @@ class JobOrderController extends Controller ], 422); } - // validated! save the entity - $em->persist($obj); - - // the event - $event = new JOEvent(); - $event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::CREATE) - ->setUser($this->getUser()) - ->setJobOrder($obj); - $em->persist($event); - - $em->flush(); - // return successful response return $this->json([ 'success' => 'Changes have been saved!' @@ -2482,7 +2350,7 @@ class JobOrderController extends Controller return null; } - public function generateInvoice(Request $req, InvoiceCreator $ic) + public function generateInvoice(Request $req, InvoiceGeneratorInterface $ic) { // error_log('generating invoice...'); $error = false; @@ -2548,7 +2416,7 @@ class JobOrderController extends Controller } // generate the invoice - $iobj = $ic->processCriteria($criteria); + $iobj = $ic->generateInvoice($criteria); // use invoice object values in a json friendly array $invoice = [ diff --git a/src/Controller/RAPIController.php b/src/Controller/RAPIController.php index ddd66d5e..67a598a6 100644 --- a/src/Controller/RAPIController.php +++ b/src/Controller/RAPIController.php @@ -25,7 +25,7 @@ use App\Ramcar\InvoiceStatus; use App\Ramcar\ModeOfPayment; use App\Ramcar\JOEventType; -use App\Service\InvoiceCreator; +use App\Service\InvoiceGeneratorInterface; use App\Service\MQTTClient; use App\Entity\RiderSession; @@ -739,7 +739,7 @@ class RAPIController extends Controller error_log(print_r($all, true)); } - public function changeService(Request $req, InvoiceCreator $ic) + public function changeService(Request $req, InvoiceGeneratorInterface $ic) { $this->debugRequest($req); @@ -839,8 +839,7 @@ class RAPIController extends Controller error_log('adding entry for battery - ' . $battery->getID()); } - $invoice = $ic->processCriteria($crit); - $invoice->setStatus(InvoiceStatus::DRAFT); + $invoice = $ic->generateInvoice($crit); // remove previous invoice $old_invoice = $jo->getInvoice(); diff --git a/src/Service/InvoiceCreator.php b/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php similarity index 80% rename from src/Service/InvoiceCreator.php rename to src/Service/InvoiceGenerator/CMBInvoiceGenerator.php index 1c6a74a7..00f4cac3 100644 --- a/src/Service/InvoiceCreator.php +++ b/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php @@ -1,8 +1,11 @@ security = $security; } - public function getVATAmount($price) + public function generateInvoice(InvoiceCriteria $criteria) { - $vat_ex_price = $this->getVATExclusivePrice($price); + // 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(); + // error_log($stype); + switch ($stype) + { + case ServiceType::JUMPSTART_TROUBLESHOOT: + $this->processJumpstart($total, $invoice); + break; + case ServiceType::JUMPSTART_WARRANTY: + $this->processJumpstartWarranty($total, $invoice); + + case ServiceType::BATTERY_REPLACEMENT_NEW: + $this->processEntries($total, $criteria, $invoice); + /* + $this->processBatteries($total, $criteria, $invoice); + $this->processTradeIns($total, $criteria, $invoice); + */ + $this->processDiscount($total, $criteria, $invoice); + 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); + // $this->processOtherServices($total, $invoice, $stype); + break; + case ServiceType::OVERHEAT_ASSISTANCE: + $this->processOverheat($total, $invoice, $cv, $has_coolant); + break; + case ServiceType::EMERGENCY_REFUEL: + error_log('processing refuel'); + $ftype = $criteria->getCustomerVehicle()->getFuelType(); + $this->processRefuel($total, $invoice, $cv); + break; + } + + // TODO: check if any promo is applied + // apply discounts + $promos = $criteria->getPromos(); + + // get current user + $user = $this->security->getUser(); + if ($user != null) + { + $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); + + // dump + //Debug::dump($invoice, 1); + + return $invoice; + } + + protected function getTaxAmount($price) + { + $vat_ex_price = $this->getTaxExclusivePrice($price); return $price - $vat_ex_price; - // return round($vat_ex_price * self::VAT_RATE, 2); + // return round($vat_ex_price * self::TAX_RATE, 2); } - public function getVATExclusivePrice($price) + protected function getTaxExclusivePrice($price) { - return round($price / (1 + self::VAT_RATE), 2); + return round($price / (1 + self::TAX_RATE), 2); } - public function getTradeInRate($ti) + protected function getTradeInRate($ti) { $size = $ti['size']; $trade_in = $ti['trade_in']; @@ -109,8 +206,8 @@ class InvoiceCreator $qty = $con_data['qty']; $sell_price = $batt->getSellingPrice(); - $vat = $this->getVATAmount($sell_price); - // $vat_ex_price = $this->getVATExclusivePrice($sell_price); + $vat = $this->getTaxAmount($sell_price); + // $vat_ex_price = $this->getTaxExclusivePrice($sell_price); $total['sell_price'] += $sell_price * $qty; $total['vat'] += $vat * $qty; @@ -192,22 +289,22 @@ class InvoiceCreator $invoice->setPromo($promo); } - public function processJumpstart(&$total, $invoice) + protected function processJumpstart(&$total, $invoice) { // add troubleshooting fee $item = new InvoiceItem(); $item->setInvoice($invoice) ->setTitle('Troubleshooting fee') ->setQuantity(1) - ->setPrice(150.00); + ->setPrice(self::TROUBLESHOOTING_FEE); $invoice->addItem($item); - $total['sell_price'] = 150.00; - $total['vat_ex_price'] = 150.00; - $total['total_price'] = 150.00; + $total['sell_price'] = self::TROUBLESHOOTING_FEE; + $total['vat_ex_price'] = self::TROUBLESHOOTING_FEE; + $total['total_price'] = self::TROUBLESHOOTING_FEE; } - public function processJumpstartWarranty(&$total, $invoice) + protected function processJumpstartWarranty(&$total, $invoice) { $item = new InvoiceItem(); $item->setInvoice($invoice) @@ -217,33 +314,33 @@ class InvoiceCreator $invoice->addItem($item); } - public function processRecharge(&$total, $invoice) + protected function processRecharge(&$total, $invoice) { // add recharge fee $item = new InvoiceItem(); $item->setInvoice($invoice) ->setTitle('Recharge fee') ->setQuantity(1) - ->setPrice(300.00); + ->setPrice(self::RECHARGE_FEE); $invoice->addItem($item); - $total['sell_price'] = 300.00; - $total['vat_ex_price'] = 300.00; - $total['total_price'] = 300.00; + $total['sell_price'] = self::RECHARGE_FEE; + $total['vat_ex_price'] = self::RECHARGE_FEE; + $total['total_price'] = self::RECHARGE_FEE; } - public function processReplacement(&$total, $invoice) + protected function processReplacement(&$total, $invoice) { // add recharge fee $item = new InvoiceItem(); $item->setInvoice($invoice) ->setTitle('Battery replacement') ->setQuantity(1) - ->setPrice(0.00); + ->setPrice(self::BATT_REPLACEMENT_FEE); $invoice->addItem($item); } - public function processWarranty(&$total, InvoiceCriteria $criteria, $invoice) + protected function processWarranty(&$total, InvoiceCriteria $criteria, $invoice) { // error_log('processing warranty'); $entries = $criteria->getEntries(); @@ -254,25 +351,25 @@ class InvoiceCreator $item->setInvoice($invoice) ->setTitle($batt->getModel()->getName() . ' ' . $batt->getSize()->getName() . ' - Service Unit') ->setQuantity(1) - ->setPrice(0.00) + ->setPrice(self::WARRANTY_FEE) ->setBattery($batt); $invoice->addItem($item); } } - public function processOtherServices(&$total, $invoice, $stype) + protected function processOtherServices(&$total, $invoice, $stype) { $item = new InvoiceItem(); $item->setInvoice($invoice) ->setTitle('Service - ' . ServiceType::getName($stype)) ->setQuantity(1) - ->setPrice(200.00); + ->setPrice(self::OTHER_SERVICES_FEE); $invoice->addItem($item); $total['total_price'] = 200.00; } - public function processOverheat(&$total, $invoice, $cv, $has_coolant) + protected function processOverheat(&$total, $invoice, $cv, $has_coolant) { // free if they have a motolite battery if ($cv->hasMotoliteBattery()) @@ -295,20 +392,21 @@ class InvoiceCreator $coolant->setInvoice($invoice) ->setTitle('4L Coolant') ->setQuantity(1) - ->setPrice(1600); + ->setPrice(self::COOLANT_FEE); $invoice->addItem($coolant); - $total_price += 1600; + $total_price += self::COOLANT_FEE; + //$total_price += 1600; } - $vat_ex_price = $this->getVATExclusivePrice($total_price); + $vat_ex_price = $this->getTaxExclusivePrice($total_price); $vat = $total_price - $vat_ex_price; $total['total_price'] = $total_price; $total['vat_ex_price'] = $vat_ex_price; $total['vat'] = $vat; } - public function processTireRepair(&$total, $invoice, $cv) + protected function processTireRepair(&$total, $invoice, $cv) { // free if they have a motolite battery if ($cv->hasMotoliteBattery()) @@ -324,14 +422,14 @@ class InvoiceCreator $invoice->addItem($item); $total_price = $fee; - $vat_ex_price = $this->getVATExclusivePrice($total_price); + $vat_ex_price = $this->getTaxExclusivePrice($total_price); $vat = $total_price - $vat_ex_price; $total['total_price'] = $total_price; $total['vat_ex_price'] = $vat_ex_price; $total['vat'] = $vat; } - public function processRefuel(&$total, $invoice, $cv) + protected function processRefuel(&$total, $invoice, $cv) { // free if they have a motolite battery if ($cv->hasMotoliteBattery()) @@ -351,8 +449,8 @@ class InvoiceCreator $total_price = $fee; // $total['total_price'] = 200.00; - $gas_price = 260; - $diesel_price = 220; + $gas_price = self::REFUEL_FEE_GAS; + $diesel_price = self::REFUEL_FEE_DIESEL; $fuel = new InvoiceItem(); error_log('fuel type - ' . $ftype); @@ -385,84 +483,11 @@ class InvoiceCreator break; } - $vat_ex_price = $this->getVATExclusivePrice($total_price); + $vat_ex_price = $this->getTaxExclusivePrice($total_price); $vat = $total_price - $vat_ex_price; $total['total_price'] = $total_price; $total['vat_ex_price'] = $vat_ex_price; $total['vat'] = $vat; } - public function processCriteria(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(); - // error_log($stype); - switch ($stype) - { - case ServiceType::JUMPSTART_TROUBLESHOOT: - $this->processJumpstart($total, $invoice); - break; - case ServiceType::JUMPSTART_WARRANTY: - $this->processJumpstartWarranty($total, $invoice); - - case ServiceType::BATTERY_REPLACEMENT_NEW: - $this->processEntries($total, $criteria, $invoice); - /* - $this->processBatteries($total, $criteria, $invoice); - $this->processTradeIns($total, $criteria, $invoice); - */ - $this->processDiscount($total, $criteria, $invoice); - 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); - // $this->processOtherServices($total, $invoice, $stype); - break; - case ServiceType::OVERHEAT_ASSISTANCE: - $this->processOverheat($total, $invoice, $cv, $has_coolant); - break; - case ServiceType::EMERGENCY_REFUEL: - error_log('processing refuel'); - $ftype = $criteria->getCustomerVehicle()->getFuelType(); - $this->processRefuel($total, $invoice, $cv); - break; - } - - // TODO: check if any promo is applied - // apply discounts - $promos = $criteria->getPromos(); - - $invoice->setTotalPrice($total['total_price']) - ->setVATExclusivePrice($total['vat_ex_price']) - ->setVAT($total['vat']) - ->setDiscount($total['discount']) - ->setTradeIn($total['ti_rate']); - - - // dump - //Debug::dump($invoice, 1); - - return $invoice; - } } diff --git a/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php b/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php new file mode 100644 index 00000000..ed936c8a --- /dev/null +++ b/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php @@ -0,0 +1,493 @@ +security = $security; + } + + 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(); + // error_log($stype); + switch ($stype) + { + case ServiceType::JUMPSTART_TROUBLESHOOT: + $this->processJumpstart($total, $invoice); + break; + case ServiceType::JUMPSTART_WARRANTY: + $this->processJumpstartWarranty($total, $invoice); + + case ServiceType::BATTERY_REPLACEMENT_NEW: + $this->processEntries($total, $criteria, $invoice); + /* + $this->processBatteries($total, $criteria, $invoice); + $this->processTradeIns($total, $criteria, $invoice); + */ + $this->processDiscount($total, $criteria, $invoice); + 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); + // $this->processOtherServices($total, $invoice, $stype); + break; + case ServiceType::OVERHEAT_ASSISTANCE: + $this->processOverheat($total, $invoice, $cv, $has_coolant); + break; + case ServiceType::EMERGENCY_REFUEL: + error_log('processing refuel'); + $ftype = $criteria->getCustomerVehicle()->getFuelType(); + $this->processRefuel($total, $invoice, $cv); + break; + } + + // TODO: check if any promo is applied + // apply discounts + $promos = $criteria->getPromos(); + + // get current user + $user = $this->security->getUser(); + if ($user != null) + { + $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); + + // dump + //Debug::dump($invoice, 1); + + return $invoice; + } + + protected function getTaxAmount($price) + { + $vat_ex_price = $this->getTaxExclusivePrice($price); + return $price - $vat_ex_price; + // return round($vat_ex_price * self::TAX_RATE, 2); + } + + protected function getTaxExclusivePrice($price) + { + return round($price / (1 + self::TAX_RATE), 2); + } + + 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; + } + + protected function processEntries(&$total, InvoiceCriteria $criteria, Invoice $invoice) + { + // error_log('processing entries...'); + $entries = $criteria->getEntries(); + + $con_batts = []; + $con_tis = []; + foreach ($entries as $entry) + { + $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 + $ti_key = $size->getID() . '|' . $trade_in; + if (!isset($con_tis[$ti_key])) + $con_tis[$ti_key] = [ + 'size' => $size, + 'trade_in' => $trade_in, + 'qty' => 0 + ]; + $con_tis[$ti_key]['qty']++; + } + + $this->processBatteries($total, $con_batts, $invoice); + $this->processTradeIns($total, $con_tis, $invoice); + } + + protected function processBatteries(&$total, $con_batts, Invoice $invoice) + { + // process batteries + foreach ($con_batts as $con_data) + { + $batt = $con_data['batt']; + $qty = $con_data['qty']; + + $sell_price = $batt->getSellingPrice(); + $vat = $this->getTaxAmount($sell_price); + // $vat_ex_price = $this->getTaxExclusivePrice($sell_price); + + $total['sell_price'] += $sell_price * $qty; + $total['vat'] += $vat * $qty; + $total['vat_ex_price'] += ($sell_price - $vat) * $qty; + + $total['total_price'] += $sell_price * $qty; + + // add item + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle($batt->getModel()->getName() . ' ' . $batt->getSize()->getName()) + ->setQuantity($qty) + ->setPrice($sell_price) + ->setBattery($batt); + + $invoice->addItem($item); + } + } + + protected function processTradeIns(&$total, $con_tis, Invoice $invoice) + { + foreach ($con_tis as $ti) + { + $qty = $ti['qty']; + $ti_rate = $this->getTradeInRate($ti); + + $total['ti_rate'] += $ti_rate * $qty; + $total['total_price'] -= $ti_rate * $qty; + + // add item + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle('Trade-in ' . TradeInType::getName($ti['trade_in']) . ' ' . $ti['size']->getName() . ' battery') + ->setQuantity($qty) + ->setPrice($ti_rate * -1); + + $invoice->addItem($item); + } + } + + protected function processDiscount(&$total, InvoiceCriteria $criteria, Invoice $invoice) + { + $promos = $criteria->getPromos(); + if (count($promos) < 1) + return; + + // NOTE: only get first promo because only one is applicable anyway + $promo = $promos[0]; + + $rate = $promo->getDiscountRate(); + $apply_to = $promo->getDiscountApply(); + + switch ($apply_to) + { + case DiscountApply::SRP: + $discount = round($total['sell_price'] * $rate, 2); + break; + case DiscountApply::OPL: + // $discount = round($total['sell_price'] * 0.6 / 0.7 * $rate, 2); + $discount = round($total['sell_price'] * (1 - 1.5 / 0.7 * $rate), 2); + break; + } + + // if discount is higher than 0, display in invoice + if ($discount > 0) + { + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle('Promo discount') + ->setQuantity(1) + ->setPrice(-1 * $discount); + $invoice->addItem($item); + } + + $total['discount'] = $discount; + $total['total_price'] -= $discount; + + // process + $invoice->setPromo($promo); + } + + protected function processJumpstart(&$total, $invoice) + { + // add troubleshooting fee + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle('Troubleshooting fee') + ->setQuantity(1) + ->setPrice(self::TROUBLESHOOTING_FEE); + $invoice->addItem($item); + + $total['sell_price'] = self::TROUBLESHOOTING_FEE; + $total['vat_ex_price'] = self::TROUBLESHOOTING_FEE; + $total['total_price'] = self::TROUBLESHOOTING_FEE; + } + + protected function processJumpstartWarranty(&$total, $invoice) + { + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle('Troubleshooting fee') + ->setQuantity(1) + ->setPrice(0.00); + $invoice->addItem($item); + } + + protected function processRecharge(&$total, $invoice) + { + // add recharge fee + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle('Recharge fee') + ->setQuantity(1) + ->setPrice(self::RECHARGE_FEE); + $invoice->addItem($item); + + $total['sell_price'] = self::RECHARGE_FEE; + $total['vat_ex_price'] = self::RECHARGE_FEE; + $total['total_price'] = self::RECHARGE_FEE; + } + + protected function processReplacement(&$total, $invoice) + { + // add recharge fee + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle('Battery replacement') + ->setQuantity(1) + ->setPrice(self::BATT_REPLACEMENT_FEE); + $invoice->addItem($item); + } + + protected function processWarranty(&$total, InvoiceCriteria $criteria, $invoice) + { + // error_log('processing warranty'); + $entries = $criteria->getEntries(); + foreach ($entries as $entry) + { + $batt = $entry['battery']; + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle($batt->getModel()->getName() . ' ' . $batt->getSize()->getName() . ' - Service Unit') + ->setQuantity(1) + ->setPrice(self::WARRANTY_FEE) + ->setBattery($batt); + $invoice->addItem($item); + } + } + + protected function processOtherServices(&$total, $invoice, $stype) + { + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle('Service - ' . ServiceType::getName($stype)) + ->setQuantity(1) + ->setPrice(self::OTHER_SERVICES_FEE); + $invoice->addItem($item); + + $total['total_price'] = 200.00; + } + + protected function processOverheat(&$total, $invoice, $cv, $has_coolant) + { + // free if they have a motolite battery + if ($cv->hasMotoliteBattery()) + $fee = 0; + else + $fee = self::SERVICE_FEE; + + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle('Service - Overheat Assistance') + ->setQuantity(1) + ->setPrice($fee); + $invoice->addItem($item); + + $total_price = $fee; + + if ($has_coolant) + { + $coolant = new InvoiceItem(); + $coolant->setInvoice($invoice) + ->setTitle('4L Coolant') + ->setQuantity(1) + ->setPrice(self::COOLANT_FEE); + $invoice->addItem($coolant); + + $total_price += self::COOLANT_FEE; + //$total_price += 1600; + } + + $vat_ex_price = $this->getTaxExclusivePrice($total_price); + $vat = $total_price - $vat_ex_price; + $total['total_price'] = $total_price; + $total['vat_ex_price'] = $vat_ex_price; + $total['vat'] = $vat; + } + + protected function processTireRepair(&$total, $invoice, $cv) + { + // free if they have a motolite battery + if ($cv->hasMotoliteBattery()) + $fee = 0; + else + $fee = self::SERVICE_FEE; + + $item = new InvoiceItem(); + $item->setInvoice($invoice) + ->setTitle('Service - Flat Tire') + ->setQuantity(1) + ->setPrice($fee); + $invoice->addItem($item); + $total_price = $fee; + + $vat_ex_price = $this->getTaxExclusivePrice($total_price); + $vat = $total_price - $vat_ex_price; + $total['total_price'] = $total_price; + $total['vat_ex_price'] = $vat_ex_price; + $total['vat'] = $vat; + } + + protected function processRefuel(&$total, $invoice, $cv) + { + // free if they have a motolite battery + if ($cv->hasMotoliteBattery()) + $fee = 0; + else + $fee = self::SERVICE_FEE; + + $ftype = $cv->getFuelType(); + $item = new InvoiceItem(); + + // service charge + $item->setInvoice($invoice) + ->setTitle('Service - ' . ServiceType::getName(ServiceType::EMERGENCY_REFUEL)) + ->setQuantity(1) + ->setPrice($fee); + $invoice->addItem($item); + $total_price = $fee; + // $total['total_price'] = 200.00; + + $gas_price = self::REFUEL_FEE_GAS; + $diesel_price = self::REFUEL_FEE_DIESEL; + + $fuel = new InvoiceItem(); + error_log('fuel type - ' . $ftype); + switch ($ftype) + { + case FuelType::GAS: + $fuel->setInvoice($invoice) + ->setTitle('4L Fuel - Gas') + ->setQuantity(1) + ->setPrice($gas_price); + $invoice->addItem($fuel); + $total_price += $gas_price; + break; + case FuelType::DIESEL: + $fuel->setInvoice($invoice) + ->setTitle('4L Fuel - Diesel') + ->setQuantity(1) + ->setPrice($diesel_price); + $total_price += $diesel_price; + $invoice->addItem($fuel); + break; + default: + // NOTE: should never get to this point + $fuel->setInvoice($invoice) + ->setTitle('Fuel - Unknown') + ->setQuantity(1) + ->setPrice(0); + $total_price += 0.00; + $invoice->addItem($fuel); + break; + } + + $vat_ex_price = $this->getTaxExclusivePrice($total_price); + $vat = $total_price - $vat_ex_price; + $total['total_price'] = $total_price; + $total['vat_ex_price'] = $vat_ex_price; + $total['vat'] = $vat; + } + +} diff --git a/src/Service/InvoiceGeneratorInterface.php b/src/Service/InvoiceGeneratorInterface.php new file mode 100644 index 00000000..b5fee76f --- /dev/null +++ b/src/Service/InvoiceGeneratorInterface.php @@ -0,0 +1,14 @@ +em = $em; + $this->ic = $ic; + $this->security = $security; + $this->validator = $validator; + } + public function generateJobOrder(JobOrder $jo, $promo_id, $invoice_change, $invoice_items, &$error_array) + { + // TODO: data validation to be moved here + + // check if invoice changed + if ($invoice_change) + { + $this->processInvoice($jo, $promo_id, $invoice_items, $error_array); + } + + // validate + $errors = $this->validator->validate($jo); + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if errors are found + if (empty($error_array)) + { + // validated, no error. save the job order + $this->em->persist($jo); + + // get current user + $user = $this->security->getUser(); + + // the event + $event = new JOEvent(); + $event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::CREATE) + ->setJobOrder($jo); + + if ($user != null) + { + $event->setUser($user); + } + + $this->em->persist($event); + + $this->em->flush(); + } + + return $error_array; + } + + protected function processInvoice($jo, $promo_id, $invoice_items, &$error_array) + { + // instantiate the invoice criteria + $criteria = new InvoiceCriteria(); + $criteria->setServiceType($jo->getServiceType()) + ->setCustomerVehicle($jo->getCustomerVehicle()); + + $ierror = $this->invoicePromo($criteria, $promo_id); + + if (!$ierror && !empty($invoice_items)) + { + // check for trade-in so we can mark it for mobile app + foreach ($invoice_items as $item) + { + // get first trade-in + if (!empty($item['trade_in'])) + { + $jo->getTradeInType($item['trade_in']); + break; + } + } + + $ierror = $this->invoiceBatteries($criteria, $invoice_items); + } + + if ($ierror) + { + $error_array['invoice'] = $ierror; + } + else + { + // generate the invoice + $iobj = $this->ic->generateInvoice($criteria); + + // validate + $ierrors = $this->validator->validate($iobj); + + // add errors to list + foreach ($ierrors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if invoice already exists for JO + $old_invoice = $jo->getInvoice(); + if ($old_invoice != null) + { + // remove old invoice + $this->em->remove($old_invoice); + $this->em->flush(); + } + + // add invoice to JO + $jo->setInvoice($iobj); + + $this->em->persist($iobj); + + } + } + + protected function invoicePromo(InvoiceCriteria $criteria, $promo_id) + { + // return error if there's a problem, false otherwise + // check service type + $stype = $criteria->getServiceType(); + if ($stype != ServiceType::BATTERY_REPLACEMENT_NEW) + return null; + + + if (empty($promo_id)) + { + return false; + } + + // check if this is a valid promo + $promo = $this->em->getRepository(Promo::class)->find($promo_id); + + if (empty($promo)) + return 'Invalid promo specified.'; + + $criteria->addPromo($promo); + return false; + } + + protected function invoiceBatteries(InvoiceCriteria $criteria, $items) + { + // check service type + $stype = $criteria->getServiceType(); + if ($stype != ServiceType::BATTERY_REPLACEMENT_NEW && $stype != ServiceType::BATTERY_REPLACEMENT_WARRANTY) + return null; + + // return error if there's a problem, false otherwise + if (!empty($items)) + { + foreach ($items as $item) + { + // check if this is a valid battery + $battery = $this->em->getRepository(Battery::class)->find($item['battery']); + + if (empty($battery)) + { + $error = 'Invalid battery specified.'; + return $error; + } + + // quantity + $qty = $item['quantity']; + if ($qty < 1) + continue; + + /* + // add to criteria + $criteria->addBattery($battery, $qty); + */ + + // if this is a trade in, add trade in + if (!empty($item['trade_in']) && TradeInType::validate($item['trade_in'])) + $trade_in = $item['trade_in']; + else + $trade_in = null; + + $criteria->addEntry($battery, $trade_in, $qty); + } + } + + return null; + } +} diff --git a/src/Service/JobOrderGenerator/ResqJobOrderGenerator.php b/src/Service/JobOrderGenerator/ResqJobOrderGenerator.php new file mode 100644 index 00000000..118dbe97 --- /dev/null +++ b/src/Service/JobOrderGenerator/ResqJobOrderGenerator.php @@ -0,0 +1,209 @@ +em = $em; + $this->ic = $ic; + $this->security = $security; + $this->validator = $validator; + } + public function generateJobOrder(JobOrder $jo, $promo_id, $invoice_change, $invoice_items, &$error_array) + { + // TODO: data validation to be moved here + + // check if invoice changed + if ($invoice_change) + { + $this->processInvoice($jo, $promo_id, $invoice_items, $error_array); + } + + // validate + $errors = $this->validator->validate($jo); + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if errors are found + if (empty($error_array)) + { + // validated, no error. save the job order + $this->em->persist($jo); + + // get current user + $user = $this->security->getUser(); + + // the event + $event = new JOEvent(); + $event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::CREATE) + ->setJobOrder($jo); + + if ($user != null) + { + $event->setUser($user); + } + + $this->em->persist($event); + + $this->em->flush(); + } + + return $error_array; + } + + protected function processInvoice($jo, $promo_id, $invoice_items, &$error_array) + { + // instantiate the invoice criteria + $criteria = new InvoiceCriteria(); + $criteria->setServiceType($jo->getServiceType()) + ->setCustomerVehicle($jo->getCustomerVehicle()); + + $ierror = $this->invoicePromo($criteria, $promo_id); + + if (!$ierror && !empty($invoice_items)) + { + // check for trade-in so we can mark it for mobile app + foreach ($invoice_items as $item) + { + // get first trade-in + if (!empty($item['trade_in'])) + { + $jo->getTradeInType($item['trade_in']); + break; + } + } + + $ierror = $this->invoiceBatteries($criteria, $invoice_items); + } + + if ($ierror) + { + $error_array['invoice'] = $ierror; + } + else + { + // generate the invoice + $iobj = $this->ic->generateInvoice($criteria); + + // validate + $ierrors = $this->validator->validate($iobj); + + // add errors to list + foreach ($ierrors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if invoice already exists for JO + $old_invoice = $jo->getInvoice(); + if ($old_invoice != null) + { + // remove old invoice + $this->em->remove($old_invoice); + $this->em->flush(); + } + + // add invoice to JO + $jo->setInvoice($iobj); + + $this->em->persist($iobj); + + } + } + + protected function invoicePromo(InvoiceCriteria $criteria, $promo_id) + { + // return error if there's a problem, false otherwise + // check service type + $stype = $criteria->getServiceType(); + if ($stype != ServiceType::BATTERY_REPLACEMENT_NEW) + return null; + + + if (empty($promo_id)) + { + return false; + } + + // check if this is a valid promo + $promo = $this->em->getRepository(Promo::class)->find($promo_id); + + if (empty($promo)) + return 'Invalid promo specified.'; + + $criteria->addPromo($promo); + return false; + } + + protected function invoiceBatteries(InvoiceCriteria $criteria, $items) + { + // check service type + $stype = $criteria->getServiceType(); + if ($stype != ServiceType::BATTERY_REPLACEMENT_NEW && $stype != ServiceType::BATTERY_REPLACEMENT_WARRANTY) + return null; + + // return error if there's a problem, false otherwise + if (!empty($items)) + { + foreach ($items as $item) + { + // check if this is a valid battery + $battery = $this->em->getRepository(Battery::class)->find($item['battery']); + + if (empty($battery)) + { + $error = 'Invalid battery specified.'; + return $error; + } + + // quantity + $qty = $item['quantity']; + if ($qty < 1) + continue; + + /* + // add to criteria + $criteria->addBattery($battery, $qty); + */ + + // if this is a trade in, add trade in + if (!empty($item['trade_in']) && TradeInType::validate($item['trade_in'])) + $trade_in = $item['trade_in']; + else + $trade_in = null; + + $criteria->addEntry($battery, $trade_in, $qty); + } + } + + return null; + } +} diff --git a/src/Service/JobOrderGeneratorInterface.php b/src/Service/JobOrderGeneratorInterface.php new file mode 100644 index 00000000..da4090cb --- /dev/null +++ b/src/Service/JobOrderGeneratorInterface.php @@ -0,0 +1,13 @@ +