diff --git a/src/Controller/CustomerAppAPI/InvoiceController.php b/src/Controller/CustomerAppAPI/InvoiceController.php index 2e9779eb..67522c96 100644 --- a/src/Controller/CustomerAppAPI/InvoiceController.php +++ b/src/Controller/CustomerAppAPI/InvoiceController.php @@ -14,6 +14,7 @@ use App\Ramcar\TransactionOrigin; use App\Entity\CustomerVehicle; use App\Entity\Promo; use App\Entity\Battery; +use App\Entity\BatterySize; use App\Entity\Customer; use App\Entity\CustomerMetadata; @@ -119,7 +120,8 @@ class InvoiceController extends ApiController 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); + $ti_batt_size_obj = $ti_batt_obj->getSize(); + $icrit->addTradeInEntry($ti_batt_size_obj, $trade_in_type, 1); } } diff --git a/src/Controller/CustomerAppAPI/JobOrderController.php b/src/Controller/CustomerAppAPI/JobOrderController.php index 4f5c95ae..64da86e5 100644 --- a/src/Controller/CustomerAppAPI/JobOrderController.php +++ b/src/Controller/CustomerAppAPI/JobOrderController.php @@ -35,6 +35,7 @@ use App\Ramcar\WarrantyClass; use App\Ramcar\HubCriteria; use App\Ramcar\DeliveryStatus; use App\Entity\Battery; +use App\Entity\BatterySize; use App\Entity\Hub; use App\Entity\Promo; use App\Entity\JOEvent; @@ -696,7 +697,8 @@ class JobOrderController extends ApiController 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); + $ti_batt_size_obj = $ti_batt_obj->getSize(); + $icrit->addTradeInEntry($ti_batt_size_obj, $trade_in_type, 1); } } @@ -1157,7 +1159,8 @@ class JobOrderController extends ApiController 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); + $battery_size = $ti_batt_obj->getSize(); + $icrit->addTradeInEntry($battery_size, $trade_in_type, 1); } } diff --git a/src/Entity/InvoiceItem.php b/src/Entity/InvoiceItem.php index 57758722..c5a92c0f 100644 --- a/src/Entity/InvoiceItem.php +++ b/src/Entity/InvoiceItem.php @@ -50,10 +50,24 @@ class InvoiceItem */ protected $battery; + // battery size for trade in items + /** + * @ORM\ManyToOne(targetEntity="BatterySize") + * @ORM\JoinColumn(name="battery_size_id", referencedColumnName="id") + */ + protected $battery_size; + + // trade in type + /** + * @ORM\Column(type="string", length=20) + */ + protected $trade_in_type; + public function __construct() { $this->title = ''; $this->price = 0.0; + $this->trade_in_type = ''; } public function getID() @@ -115,4 +129,26 @@ class InvoiceItem { return $this->battery; } + + public function setBatterySize(BatterySize $battery_size) + { + $this->battery_size = $battery_size; + return $this; + } + + public function getBatterySize() + { + return $this->battery_size; + } + + public function setTradeInType(string $trade_in_type) + { + $this->trade_in_type = $trade_in_type; + return $this; + } + + public function getTradeInType() + { + return $this->trade_in_type; + } } diff --git a/src/Entity/Role.php b/src/Entity/Role.php index 38fcc0b2..f90efd39 100644 --- a/src/Entity/Role.php +++ b/src/Entity/Role.php @@ -6,6 +6,7 @@ use Catalyst\AuthBundle\Entity\Role as BaseRole; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; +use Symfony\Component\Validator\Constraints as Assert; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; /** @@ -16,6 +17,19 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; */ class Role extends BaseRole { + /** + * @ORM\Id + * @ORM\Column(type="string", length=80) + * @Assert\NotBlank() + */ + protected $id; + + /** + * @ORM\Column(type="string", length=80) + * @Assert\NotBlank() + */ + protected $name; + /** * @ORM\ManyToMany(targetEntity="User", mappedBy="roles", fetch="EXTRA_LAZY") */ diff --git a/src/InvoiceRule/BatteryReplacementWarranty.php b/src/InvoiceRule/BatteryReplacementWarranty.php index 46ba5e8a..9bdaaaa9 100644 --- a/src/InvoiceRule/BatteryReplacementWarranty.php +++ b/src/InvoiceRule/BatteryReplacementWarranty.php @@ -116,14 +116,12 @@ class BatteryReplacementWarranty implements InvoiceRuleInterface $qty = $item['quantity']; if ($qty < 1) continue; - - // 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 + + if (empty($item['trade_in'])) + { $trade_in = null; - - $criteria->addEntry($battery, $trade_in, $qty); + $criteria->addEntry($battery, $trade_in, $qty); + } } } diff --git a/src/InvoiceRule/BatterySales.php b/src/InvoiceRule/BatterySales.php index f3b66c11..7938fe6e 100644 --- a/src/InvoiceRule/BatterySales.php +++ b/src/InvoiceRule/BatterySales.php @@ -103,26 +103,24 @@ class BatterySales implements InvoiceRuleInterface // check if this is a valid battery foreach ($invoice_items as $item) { - $battery = $this->em->getRepository(Battery::class)->find($item['battery']); - - if (empty($battery)) + if (isset($item['battery'])) { - $error = 'Invalid battery specified.'; - return $error; - } + $battery = $this->em->getRepository(Battery::class)->find($item['battery']); - // quantity - $qty = $item['quantity']; - if ($qty < 1) - continue; + if (empty($battery)) + { + $error = 'Invalid battery specified.'; + return $error; + } + + // quantity + $qty = $item['quantity']; + if ($qty < 1) + continue; - // 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); + $criteria->addEntry($battery, $trade_in, $qty); + } } } diff --git a/src/InvoiceRule/TradeIn.php b/src/InvoiceRule/TradeIn.php index 224bf098..4ea7d028 100644 --- a/src/InvoiceRule/TradeIn.php +++ b/src/InvoiceRule/TradeIn.php @@ -2,12 +2,24 @@ namespace App\InvoiceRule; +use Doctrine\ORM\EntityManagerInterface; + use App\InvoiceRuleInterface; use App\Ramcar\TradeInType; +use App\Ramcar\ServiceType; + +use App\Entity\BatterySize; class TradeIn implements InvoiceRuleInterface { + protected $em; + + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + public function getID() { return 'trade-in'; @@ -29,16 +41,7 @@ class TradeIn implements InvoiceRuleInterface if ($trade_in_type != null) { - // at this point, entry is a trade in - // need to check if battery (coming from CRM) is set - // or battery_size is set (coming from rider app) - if (isset($entry['battery'])) - { - $battery = $entry['battery']; - $batt_size = $battery->getSize(); - } - else - $batt_size = $entry['battery_size']; + $batt_size = $entry['battery_size']; $ti_rate = $this->getTradeInRate($batt_size, $trade_in_type); @@ -50,6 +53,8 @@ class TradeIn implements InvoiceRuleInterface $price = bcmul($ti_rate, -1, 2); $items[] = [ + 'battery_size' => $batt_size, + 'trade_in_type' => $trade_in_type, 'qty' => $qty, 'title' => $this->getTitle($batt_size, $trade_in_type), 'price' => $price, @@ -67,6 +72,42 @@ class TradeIn implements InvoiceRuleInterface public function validateInvoiceItems($criteria, $invoice_items) { + // check service type. Only battery sales and battery warranty should have invoice items. + $stype = $criteria->getServiceType(); + if ($stype != ServiceType::BATTERY_REPLACEMENT_NEW) + return null; + + // return error if there's a problem, false otherwise + if (!empty($invoice_items)) + { + // check if this is a valid battery + foreach ($invoice_items as $item) + { + if (isset($item['battery_size'])) + { + $battery_size = $this->em->getRepository(BatterySize::class)->find($item['battery_size']); + + if (empty($battery_size)) + { + $error = 'Invalid battery size specified.'; + return $error; + } + + // quantity + $qty = $item['quantity']; + if ($qty < 1) + continue; + + // check if trade in is set and if trade in type if valid + if (!empty($item['trade_in']) && TradeInType::validate($item['trade_in'])) + { + $trade_in = $item['trade_in']; + $criteria->addTradeInEntry($battery_size, $trade_in, $qty); + } + } + } + } + return null; } diff --git a/src/Service/InvoiceManager.php b/src/Service/InvoiceManager.php index 8f655224..da8770cc 100644 --- a/src/Service/InvoiceManager.php +++ b/src/Service/InvoiceManager.php @@ -55,7 +55,7 @@ class InvoiceManager implements InvoiceGeneratorInterface new InvoiceRule\Fuel($this->em, $this->pt_manager), new InvoiceRule\TireRepair($this->em, $this->pt_manager), new InvoiceRule\DiscountType($this->em), - new InvoiceRule\TradeIn(), + new InvoiceRule\TradeIn($this->em), new InvoiceRule\Tax($this->em, $this->pt_manager), ]; } @@ -166,6 +166,7 @@ class InvoiceManager implements InvoiceGeneratorInterface // (3) generateInvoiceCriteria // (4) RiderAPIHandler's changeService // (5) TAPI's JobOrderController + // (6) CAPI's RiderAppController public function generateInvoice($criteria) { // no need to validate since generateDraftInvoice was called before this was called @@ -214,17 +215,27 @@ class InvoiceManager implements InvoiceGeneratorInterface $price = $item['price']; $battery = null; + $battery_size = null; + $trade_in_type = ''; if (isset($item['battery'])) $battery = $item['battery']; if (isset($item['promo'])) $promo = $item['promo']; + if (isset($item['battery_size'])) + $battery_size = $item['battery_size']; + + if (isset($item['trade_in_type'])) + $trade_in_type = $item['trade_in_type']; + $invoice_items[] = [ 'title' => $title, 'quantity' => $quantity, 'price' => $price, 'battery' => $battery, + 'battery_size' => $battery_size, + 'trade_in_type' => $trade_in_type, ]; } } @@ -273,11 +284,15 @@ class InvoiceManager implements InvoiceGeneratorInterface $invoice_item->setInvoice($invoice) ->setTitle($item['title']) ->setQuantity($item['quantity']) - ->setPrice((float)$item['price']); + ->setPrice((float)$item['price']) + ->setTradeInType($item['trade_in_type']); if ($item['battery'] != null) $invoice_item->setBattery($item['battery']); + if ($item['battery_size'] != null) + $invoice_item->setBatterySize($item['battery_size']); + $invoice->addItem($invoice_item); } diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 7abba636..a688a7ba 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -30,6 +30,7 @@ use App\Entity\EmergencyType; use App\Entity\OwnershipType; use App\Entity\CustomerLocation; use App\Entity\Battery; +use App\Entity\BatterySize; use App\Ramcar\ServiceType; use App\Ramcar\TradeInType; @@ -1611,6 +1612,26 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $error_array['cust_location'] = 'Invalid customer location'; } + // check facilitated type + $fac_type = $req->request->get('facilitated_type'); + if (!empty($fac_type)) + { + if (!FacilitatedType::validate($fac_type)) + $fac_type = null; + } + else + $fac_type = null; + + // check facilitated by + $fac_by_id = $req->request->get('facilitated_by'); + $fac_by = null; + if (!empty($fac_by_id)) + { + $fac_by = $em->getRepository(Hub::class)->find($fac_by_id); + if (empty($fac_by)) + $fac_by = null; + } + // get previously assigned hub, if any $old_hub = $obj->getHub(); @@ -1671,6 +1692,8 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setOwnershipType($owner_type) ->setCustomerLocation($cust_location) ->setInventoryCount($req->request->get('hub_inv_count', 0)) + ->setFacilitatedType($fac_type) + ->setFacilitatedBy($fac_by) ->clearRider(); if ($user != null) @@ -2312,6 +2335,13 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $em = $this->em; $jo = $em->getRepository(JobOrder::class)->find($id); + // get the job order's invoice items + $invoice = $jo->getInvoice(); + $invoice_items = []; + if ($invoice != null) + $invoice_items = $invoice->getItems(); + + $params['invoice_items'] = $invoice_items; $params['obj'] = $jo; $params['mode'] = 'open_edit'; $params['cvid'] = $jo->getCustomerVehicle()->getID(); @@ -3631,15 +3661,15 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $params['trade_in_bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll(); $params['promos'] = $em->getRepository(Promo::class)->findAll(); - // list of batteries for trade-in - $ti_batteries = $em->getRepository(Battery::class)->findAll(); - $trade_in_batteries = []; - foreach ($ti_batteries as $ti_battery) + // list of battery sizes for trade-in + $ti_batt_sizes = $em->getRepository(BatterySize::class)->findAll(); + $trade_in_batt_sizes = []; + foreach ($ti_batt_sizes as $ti_batt_size) { - $battery_name = $ti_battery->getModel()->getName() . ' ' . $ti_battery->getSize()->getName(); - $trade_in_batteries[$ti_battery->getID()] = $battery_name; + $batt_size_name = $ti_batt_size->getName(); + $trade_in_batt_sizes[$ti_batt_size->getID()] = $batt_size_name; } - $params['trade_in_batteries'] = $trade_in_batteries; + $params['trade_in_batt_sizes'] = $trade_in_batt_sizes; // list of emergency types $e_types = $em->getRepository(EmergencyType::class)->findBy([], ['name' => 'ASC']); @@ -4337,12 +4367,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $phone_number = $this->country_code . $notif_number; - // check if reason is administrative - if ($rejection->getReason() == JORejectionReason::ADMINISTRATIVE) - return null; + // check if reason is in the list of rejection reason + $flag_send_sms = $this->checkRejectionReason($rejection->getReason()); - // check if reason is discount - if ($rejection->getReason() == JORejectionReason::DISCOUNT) + if (!$flag_send_sms) return null; // sms content @@ -4376,6 +4404,43 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $this->rt->sendSMS($phone_number, $this->translator->trans('message.battery_brand_allcaps'), $msg); } + protected function checkRejectionReason($reason): bool + { + // reason is administrative + if ($reason == JORejectionReason::ADMINISTRATIVE) + return false; + + // reason is discount + if ($reason == JORejectionReason::DISCOUNT) + return false; + + // reason is store closed on schedule + if ($reason == JORejectionReason::STORE_CLOSED_SCHEDULED) + return false; + + // store closed half day + if ($reason == JORejectionReason::STORE_CLOSED_HALF_DAY) + return false; + + // prio hub for warranty claim + if ($reason == JORejectionReason::PRIORITY_HUB_WTY_CLAIM) + return false; + + // prio hub for jumpstart + if ($reason == JORejectionReason::PRIORITY_HUB_JUMPSTART) + return false; + + // prio hub for RES-Q request + if ($reason == JORejectionReason::PRIORITY_HUB_RESQ_REQUEST) + return false; + + // customer request + if ($reason == JORejectionReason::CUSTOMER_REQUEST) + return false; + + return true; + } + protected function skipInventoryCheck(Point $coordinates): bool { $long = $coordinates->getLongitude(); diff --git a/templates/invoice/trade_in.html.twig b/templates/invoice/trade_in.html.twig index e033143d..4ac94b06 100644 --- a/templates/invoice/trade_in.html.twig +++ b/templates/invoice/trade_in.html.twig @@ -8,11 +8,11 @@