From 9447f643126c2ee5aba0ca254fa569f0b856931d Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Tue, 19 Dec 2023 17:30:38 +0800 Subject: [PATCH 01/29] Create item price, price tier, and item type entities for regional pricing. #780 --- src/Entity/ItemPrice.php | 53 +++++++++++++++++++++++++++++ src/Entity/ItemType.php | 69 +++++++++++++++++++++++++++++++++++++ src/Entity/PriceTier.php | 73 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 src/Entity/ItemPrice.php create mode 100644 src/Entity/ItemType.php create mode 100644 src/Entity/PriceTier.php diff --git a/src/Entity/ItemPrice.php b/src/Entity/ItemPrice.php new file mode 100644 index 00000000..d03cadbf --- /dev/null +++ b/src/Entity/ItemPrice.php @@ -0,0 +1,53 @@ +id; + } +} diff --git a/src/Entity/ItemType.php b/src/Entity/ItemType.php new file mode 100644 index 00000000..204fb9b3 --- /dev/null +++ b/src/Entity/ItemType.php @@ -0,0 +1,69 @@ +code = ''; + } + + public function getID() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + return $this; + } + + public function getName() + { + return $this->name; + } + + public function setCode($code) + { + $this->code = $code; + return $this; + } + + public function getCode() + { + return $this->code; + } +} diff --git a/src/Entity/PriceTier.php b/src/Entity/PriceTier.php new file mode 100644 index 00000000..18b735aa --- /dev/null +++ b/src/Entity/PriceTier.php @@ -0,0 +1,73 @@ +meta_areas = []; + + $this->items = new ArrayCollection(); + } + + public function getID() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + return $this; + } + + public function getName() + { + return $this->name; + } + + public function addMetaArea($id, $value) + { + $this->meta_areas[$id] = $value; + } + + public function getAllMetaAreas() + { + return $this->meta_areas; + } +} From fa3cf12be12bb8cc9fd43355a96031997dc70ff1 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Wed, 20 Dec 2023 18:15:17 +0800 Subject: [PATCH 02/29] Add controller for price tier. #780 --- config/packages/catalyst_auth.yaml | 42 +++++ config/packages/catalyst_menu.yaml | 26 ++- config/routes/price_tier.yaml | 34 ++++ src/Controller/PriceTierController.php | 244 +++++++++++++++++++++++++ src/Entity/ItemPrice.php | 37 +++- src/Entity/PriceTier.php | 29 ++- src/Entity/SupportedArea.php | 19 ++ templates/price-tier/form.html.twig | 154 ++++++++++++++++ templates/price-tier/list.html.twig | 146 +++++++++++++++ 9 files changed, 713 insertions(+), 18 deletions(-) create mode 100644 config/routes/price_tier.yaml create mode 100644 src/Controller/PriceTierController.php create mode 100644 templates/price-tier/form.html.twig create mode 100644 templates/price-tier/list.html.twig diff --git a/config/packages/catalyst_auth.yaml b/config/packages/catalyst_auth.yaml index dfaf31e0..9977d263 100644 --- a/config/packages/catalyst_auth.yaml +++ b/config/packages/catalyst_auth.yaml @@ -634,6 +634,48 @@ catalyst_auth: - id: service_offering.delete label: Delete + - id: price_tier + label: Price Tier + acls: + - id: price_tier.menu + label: Menu + - id: price_tier.list + label: List + - id: price_tier.add + label: Add + - id: price_tier.update + label: Update + - id: price_tier.delete + label: Delete + + - id: item_type + label: Item Type + acls: + - id: item.type.menu + label: Menu + - id: item.type.list + label: List + - id: item.type.add + label: Add + - id: item.type.update + label: Update + - id: item.type.delete + label: Delete + + - id: item + label: Item + acls: + - id: item.menu + label: Menu + - id: item.list + label: List + - id: item.add + label: Add + - id: item.update + label: Update + - id: item.delete + label: Delete + api: user_entity: "App\\Entity\\ApiUser" acl_data: diff --git a/config/packages/catalyst_menu.yaml b/config/packages/catalyst_menu.yaml index deb8c43f..a92f2c4e 100644 --- a/config/packages/catalyst_menu.yaml +++ b/config/packages/catalyst_menu.yaml @@ -177,7 +177,7 @@ catalyst_menu: acl: support.menu label: '[menu.support]' icon: flaticon-support - order: 10 + order: 11 - id: customer_list acl: customer.list label: '[menu.support.customers]' @@ -223,7 +223,7 @@ catalyst_menu: acl: service.menu label: '[menu.service]' icon: flaticon-squares - order: 11 + order: 12 - id: service_list acl: service.list label: '[menu.service.services]' @@ -233,7 +233,7 @@ catalyst_menu: acl: partner.menu label: '[menu.partner]' icon: flaticon-network - order: 12 + order: 13 - id: partner_list acl: partner.list label: '[menu.partner.partners]' @@ -247,7 +247,7 @@ catalyst_menu: acl: motolite_event.menu label: '[menu.motolite_event]' icon: flaticon-event-calendar-symbol - order: 13 + order: 14 - id: motolite_event_list acl: motolite_event.list label: '[menu.motolite_event.events]' @@ -257,7 +257,7 @@ catalyst_menu: acl: analytics.menu label: '[menu.analytics]' icon: flaticon-graphic - order: 14 + order: 15 - id: analytics_forecast_form acl: analytics.forecast label: '[menu.analytics.forecasting]' @@ -267,7 +267,7 @@ catalyst_menu: acl: database.menu label: '[menu.database]' icon: fa fa-database - order: 15 + order: 16 - id: ticket_type_list acl: ticket_type.menu label: '[menu.database.tickettypes]' @@ -288,3 +288,17 @@ catalyst_menu: acl: service_offering.menu label: '[menu.database.serviceofferings]' parent: database + + - id: item + acl: item.menu + label: Item Management + icon: fa fa-boxes + order: 10 + - id: price_tier_list + acl: price_tier.list + label: Price Tiers + parent: item + - id: item_list + acl: item.list + label: Items + parent: item diff --git a/config/routes/price_tier.yaml b/config/routes/price_tier.yaml new file mode 100644 index 00000000..f0127e12 --- /dev/null +++ b/config/routes/price_tier.yaml @@ -0,0 +1,34 @@ +price_tier_list: + path: /pricetiers + controller: App\Controller\PriceTierController::index + methods: [GET] + +price_tier_rows: + path: /pricetiers/rows + controller: App\Controller\PriceTierController::datatableRows + methods: [POST] + +price_tier_add_form: + path: /pricetiers/newform + controller: App\Controller\PriceTierController::addForm + methods: [GET] + +price_tier_add_submit: + path: /pricetiers + controller: App\Controller\PriceTierController::addSubmit + methods: [POST] + +price_tier_update_form: + path: /pricetiers/{id} + controller: App\Controller\PriceTierController::updateForm + methods: [GET] + +price_tier_update_submit: + path: /pricetiers/{id} + controller: App\Controller\PriceTierController::updateSubmit + methods: [POST] + +price_tier_delete: + path: /pricetiers/{id} + controller: App\Controller\PriceTierController::deleteSubmit + methods: [DELETE] diff --git a/src/Controller/PriceTierController.php b/src/Controller/PriceTierController.php new file mode 100644 index 00000000..cd08f90f --- /dev/null +++ b/src/Controller/PriceTierController.php @@ -0,0 +1,244 @@ +render('price-tier/list.html.twig'); + } + + /** + * @IsGranted("price_tier.list") + */ + public function datatableRows(Request $req) + { + // get query builder + $qb = $this->getDoctrine() + ->getRepository(PriceTier::class) + ->createQueryBuilder('q'); + + // get datatable params + $datatable = $req->request->get('datatable'); + + // count total records + $tquery = $qb->select('COUNT(q)'); + $this->setQueryFilters($datatable, $tquery); + $total = $tquery->getQuery() + ->getSingleScalarResult(); + + // get current page number + $page = $datatable['pagination']['page'] ?? 1; + + $perpage = $datatable['pagination']['perpage']; + $offset = ($page - 1) * $perpage; + + // add metadata + $meta = [ + 'page' => $page, + 'perpage' => $perpage, + 'pages' => ceil($total / $perpage), + 'total' => $total, + 'sort' => 'asc', + 'field' => 'id' + ]; + + // build query + $query = $qb->select('q'); + $this->setQueryFilters($datatable, $query); + + // check if sorting is present, otherwise use default + if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) { + $order = $datatable['sort']['sort'] ?? 'asc'; + $query->orderBy('q.' . $datatable['sort']['field'], $order); + } else { + $query->orderBy('q.id', 'asc'); + } + + // get rows for this page + $obj_rows = $query->setFirstResult($offset) + ->setMaxResults($perpage) + ->getQuery() + ->getResult(); + + // process rows + $rows = []; + foreach ($obj_rows as $orow) { + // add row data + $row['id'] = $orow->getID(); + $row['name'] = $orow->getName(); + + // add row metadata + $row['meta'] = [ + 'update_url' => '', + 'delete_url' => '' + ]; + + // add crud urls + if ($this->isGranted('price_tier.update')) + $row['meta']['update_url'] = $this->generateUrl('price_tier_update_form', ['id' => $row['id']]); + if ($this->isGranted('service_offering.delete')) + $row['meta']['delete_url'] = $this->generateUrl('price_tier_delete', ['id' => $row['id']]); + + $rows[] = $row; + } + + // response + return $this->json([ + 'meta' => $meta, + 'data' => $rows + ]); + } + + /** + * @Menu(selected="price_tier.list") + * @IsGranted("price_tier.add") + */ + public function addForm(EntityManagerInterface $em) + { + $pt = new PriceTier(); + + // get the supported areas + $sets = $this->generateFormSets($em); + + $params = [ + 'obj' => $pt, + 'sets' => $sets, + 'mode' => 'create', + ]; + + // response + return $this->render('price-tier/form.html.twig', $params); + } + + /** + * @IsGranted("price_tier.add") + */ + public function addSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator) + { + $pt = new PriceTier(); + + // TODO: add validation for supported area + $this->setObject($pt, $req); + + // validate + $errors = $validator->validate($pt); + + // initialize error list + $error_array = []; + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // validated! save the entity + $em->persist($pt); + + // set the price tier id for the selected supported areas + $this->updateSupportedAreas($em, $pt, $req); + + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + /** + * @Menu(selected="price_tier_list") + * @ParamConverter("price_tier", class="App\Entity\PriceTier") + * @IsGranted("price_tier.update") + */ + public function updateForm($id, EntityManagerInterface $em, PriceTier $pt) + { + // get the supported areas + $sets = $this->generateFormSets($em); + + $params = [ + 'obj' => $pt, + 'sets' => $sets, + 'mode' => 'update', + ]; + + // response + return $this->render('price-tier/form.html.twig', $params); + } + + protected function setObject(PriceTier $obj, Request $req) + { + $obj->setName($req->request->get('name')); + } + + public function updateSupportedAreas(EntityManagerInterface $em, PriceTier $obj, Request $req) + { + // get the selected areas + $areas = $req->request->get('areas'); + + foreach ($areas as $area_id) + { + // get supported area + $supported_area = $em->getRepository(SupportedArea::class)->find($area_id); + + if ($supported_area != null) + $supported_area->setPriceTier($obj); + } + } + + protected function generateFormSets(EntityManagerInterface $em) + { + // get the supported areas + // TODO: filter out the supported areas that already have a price tier id? but if we're editing, we need those price tiers + $areas = $em->getRepository(SupportedArea::class)->findAll(); + $areas_set = []; + foreach ($areas as $area) + { + $areas_set[$area->getID()] = $area->getName(); + } + + return [ + 'areas' => $areas_set + ]; + } + + protected function setQueryFilters($datatable, QueryBuilder $query) + { + if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) { + $query->where('q.name LIKE :filter') + ->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%'); + } + } +} diff --git a/src/Entity/ItemPrice.php b/src/Entity/ItemPrice.php index d03cadbf..b9f14517 100644 --- a/src/Entity/ItemPrice.php +++ b/src/Entity/ItemPrice.php @@ -20,7 +20,7 @@ class ItemPrice protected $id; /** - * @ORM\ManyToOne(targetEntity="PriceTier", inversedBy="items") + * @ORM\ManyToOne(targetEntity="PriceTier", inversedBy="item_prices") * @ORM\JoinColumn(name="price_tier_id", referencedColumnName="id") */ protected $price_tier; @@ -44,10 +44,43 @@ class ItemPrice /** * @ORM\Column(type="integer") */ - protected $item_price; + protected $price; public function getID() { return $this->id; } + + public function setPriceTier(PriceTier $price_tier) + { + $this->price_tier = $price_tier; + return $this; + } + + public function getPriceTier() + { + return $this->price_tier; + } + + public function setItemID($item_id) + { + $this->item_id = $item_id; + return $this; + } + + public function getItemID() + { + return $this->item_id; + } + + public function setPrice($price) + { + $this->price = $price; + return $this; + } + + public function getPrice() + { + return $this->price; + } } diff --git a/src/Entity/PriceTier.php b/src/Entity/PriceTier.php index 18b735aa..103b7290 100644 --- a/src/Entity/PriceTier.php +++ b/src/Entity/PriceTier.php @@ -28,21 +28,20 @@ class PriceTier // supported areas under price tier /** - * @ORM\Column(type="json") + * @ORM\OneToMany(targetEntity="SupportedArea", mappedBy="price_tier"); */ - protected $meta_areas; + protected $supported_areas; // items under a price tier /** * @ORM\OneToMany(targetEntity="ItemPrice", mappedBy="price_tier") */ - protected $items; + protected $item_prices; public function __construct() { - $this->meta_areas = []; - - $this->items = new ArrayCollection(); + $this->supported_areas = new ArrayCollection(); + $this->item_prices = new ArrayCollection(); } public function getID() @@ -61,13 +60,23 @@ class PriceTier return $this->name; } - public function addMetaArea($id, $value) + public function getSupportedAreaObjects() { - $this->meta_areas[$id] = $value; + return $this->supported_areas; } - public function getAllMetaAreas() + public function getSupportedAreas() { - return $this->meta_areas; + $str_supported_areas = []; + foreach ($this->supported_areas as $supported_area) + $str_supported_areas[] = $supported_area->getID(); + + return $str_supported_areas; } + + public function getItemPrices() + { + return $this->item_prices; + } + } diff --git a/src/Entity/SupportedArea.php b/src/Entity/SupportedArea.php index 0f70c39e..67d53ed8 100644 --- a/src/Entity/SupportedArea.php +++ b/src/Entity/SupportedArea.php @@ -39,9 +39,17 @@ class SupportedArea */ protected $coverage_area; + /** + * @ORM\ManyToOne(targetEntity="PriceTier", inversedBy="supported_areas") + * @ORM\JoinColumn(name="price_tier_id", referencedColumnName="id", nullable=true) + */ + protected $price_tier; + public function __construct() { $this->date_create = new DateTime(); + + $this->price_tier = null; } public function getID() @@ -82,5 +90,16 @@ class SupportedArea { return $this->coverage_area; } + + public function setPriceTier(PriceTier $price_tier) + { + $this->price_tier = $price_tier; + return $this; + } + + public function getPriceTier() + { + return $this->price_tier; + } } diff --git a/templates/price-tier/form.html.twig b/templates/price-tier/form.html.twig new file mode 100644 index 00000000..105d7ec2 --- /dev/null +++ b/templates/price-tier/form.html.twig @@ -0,0 +1,154 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

Price Tiers

+
+
+
+ +
+ +
+
+
+
+
+
+ + + +

+ {% if mode == 'update' %} + Edit Price Tier + {{ obj.getName() }} + {% else %} + New Price Tier + {% endif %} +

+
+
+
+
+
+
+ +
+ + +
+
+
+ +
+ {% if sets.areas is empty %} + No supported areas. + {% else %} +
+ {% for id, label in sets.areas %} + + {% endfor %} +
+ {% endif %} + +
+
+
+
+
+
+
+ + Back +
+
+
+
+
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/templates/price-tier/list.html.twig b/templates/price-tier/list.html.twig new file mode 100644 index 00000000..e97eed40 --- /dev/null +++ b/templates/price-tier/list.html.twig @@ -0,0 +1,146 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

+ Price Tiers +

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} From 8c810bf27a86c4b27f6f740e8c93762d1787e410 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Thu, 21 Dec 2023 16:24:37 +0800 Subject: [PATCH 03/29] Add validation and deletion for price tier. #780 --- src/Controller/PriceTierController.php | 143 ++++++++++++++++++++++--- src/Entity/PriceTier.php | 6 ++ src/Entity/SupportedArea.php | 2 +- 3 files changed, 134 insertions(+), 17 deletions(-) diff --git a/src/Controller/PriceTierController.php b/src/Controller/PriceTierController.php index cd08f90f..b44a6bb8 100644 --- a/src/Controller/PriceTierController.php +++ b/src/Controller/PriceTierController.php @@ -139,17 +139,18 @@ class PriceTierController extends Controller */ public function addSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator) { + // initialize error list + $error_array = []; + $pt = new PriceTier(); - // TODO: add validation for supported area + $error_array = $this->validateRequest($em, $req); + $this->setObject($pt, $req); // validate $errors = $validator->validate($pt); - // initialize error list - $error_array = []; - // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); @@ -180,13 +181,13 @@ class PriceTierController extends Controller /** * @Menu(selected="price_tier_list") - * @ParamConverter("price_tier", class="App\Entity\PriceTier") + * @ParamConverter("pt", class="App\Entity\PriceTier") * @IsGranted("price_tier.update") */ public function updateForm($id, EntityManagerInterface $em, PriceTier $pt) { // get the supported areas - $sets = $this->generateFormSets($em); + $sets = $this->generateFormSets($em, $pt); $params = [ 'obj' => $pt, @@ -198,31 +199,141 @@ class PriceTierController extends Controller return $this->render('price-tier/form.html.twig', $params); } + /** + * @ParamConverter("pt", class="App\Entity\PriceTier") + * @IsGranted("price_tier.update") + */ + public function updateSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator, PriceTier $pt) + { + // initialize error list + $error_array = []; + + // clear supported areas of price tier + $this->clearPriceTierSupportedAreas($em, $pt); + + $error_array = $this->validateRequest($em, $req); + $this->setObject($pt, $req); + + // validate + $errors = $validator->validate($pt); + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // set the price tier id for the selected supported areas + $this->updateSupportedAreas($em, $pt, $req); + + // validated! save the entity + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + /** + * @ParamConverter("pt", class="App\Entity\PriceTier") + * @IsGranted("price_tier.delete") + */ + public function deleteSubmit(EntityManagerInterface $em, PriceTier $pt) + { + // clear supported areas of price tier + $this->clearPriceTierSupportedAreas($em, $pt); + + // delete this object + $em->remove($pt); + $em->flush(); + + // response + $response = new Response(); + $response->setStatusCode(Response::HTTP_OK); + $response->send(); + } + + protected function validateRequest(EntityManagerInterface $em, Request $req) + { + // get areas + $areas = $req->request->get('areas'); + + // check if no areas selected aka empty + if (!empty($areas)) + { + foreach ($areas as $area_id) + { + $supported_area = $em->getRepository(SupportedArea::class)->find($area_id); + + if ($supported_area == null) + return ['areas' => 'Invalid area']; + + // check if supported area already belongs to a price tier + if ($supported_area->getPriceTier() != null) + return ['areas' => 'Area already belongs to a price tier.']; + } + } + + return null; + } + protected function setObject(PriceTier $obj, Request $req) { + // clear supported areas first + $obj->clearSupportedAreas(); + $obj->setName($req->request->get('name')); } - public function updateSupportedAreas(EntityManagerInterface $em, PriceTier $obj, Request $req) + protected function clearPriceTierSupportedAreas(EntityManagerInterface $em, PriceTier $obj) + { + // find the supported areas set with the price tier + $areas = $em->getRepository(SupportedArea::class)->findBy(['price_tier' => $obj]); + + if (!empty($areas)) + { + // set the price tier id for the supported areas to null + foreach ($areas as $area) + { + $area->setPriceTier(null); + } + + $em->flush(); + } + } + + protected function updateSupportedAreas(EntityManagerInterface $em, PriceTier $obj, Request $req) { // get the selected areas $areas = $req->request->get('areas'); - foreach ($areas as $area_id) + // check if no areas selected aka empty + if (!empty($areas)) { - // get supported area - $supported_area = $em->getRepository(SupportedArea::class)->find($area_id); + foreach ($areas as $area_id) + { + // get supported area + $supported_area = $em->getRepository(SupportedArea::class)->find($area_id); - if ($supported_area != null) - $supported_area->setPriceTier($obj); + if ($supported_area != null) + $supported_area->setPriceTier($obj); + } } } - protected function generateFormSets(EntityManagerInterface $em) + protected function generateFormSets(EntityManagerInterface $em, PriceTier $pt = null) { - // get the supported areas - // TODO: filter out the supported areas that already have a price tier id? but if we're editing, we need those price tiers - $areas = $em->getRepository(SupportedArea::class)->findAll(); + // get the supported areas with no price tier id or price tier id is set to the one that is being updated + $areas = $em->getRepository(SupportedArea::class)->findBy(['price_tier' => array(null, $pt)]); $areas_set = []; foreach ($areas as $area) { diff --git a/src/Entity/PriceTier.php b/src/Entity/PriceTier.php index 103b7290..37c8d8f8 100644 --- a/src/Entity/PriceTier.php +++ b/src/Entity/PriceTier.php @@ -74,6 +74,12 @@ class PriceTier return $str_supported_areas; } + public function clearSupportedAreas() + { + $this->supported_areas->clear(); + return $this; + } + public function getItemPrices() { return $this->item_prices; diff --git a/src/Entity/SupportedArea.php b/src/Entity/SupportedArea.php index 67d53ed8..0e176f6a 100644 --- a/src/Entity/SupportedArea.php +++ b/src/Entity/SupportedArea.php @@ -91,7 +91,7 @@ class SupportedArea return $this->coverage_area; } - public function setPriceTier(PriceTier $price_tier) + public function setPriceTier(PriceTier $price_tier = null) { $this->price_tier = $price_tier; return $this; From 58f46fd5bf90c236e7fdbc4984c68567800f7f12 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 21 Dec 2023 23:23:46 -0500 Subject: [PATCH 04/29] Add route and CRUD for item type. #780 --- config/packages/catalyst_auth.yaml | 10 +- config/packages/catalyst_menu.yaml | 4 + config/routes/item_type.yaml | 34 ++++ config/routes/price_tier.yaml | 14 +- src/Controller/ItemTypeController.php | 251 ++++++++++++++++++++++++++ templates/item-type/form.html.twig | 142 +++++++++++++++ templates/item-type/list.html.twig | 146 +++++++++++++++ translations/messages.en.yaml | 1 + 8 files changed, 590 insertions(+), 12 deletions(-) create mode 100644 config/routes/item_type.yaml create mode 100644 src/Controller/ItemTypeController.php create mode 100644 templates/item-type/form.html.twig create mode 100644 templates/item-type/list.html.twig diff --git a/config/packages/catalyst_auth.yaml b/config/packages/catalyst_auth.yaml index 9977d263..f63c5bc0 100644 --- a/config/packages/catalyst_auth.yaml +++ b/config/packages/catalyst_auth.yaml @@ -651,15 +651,15 @@ catalyst_auth: - id: item_type label: Item Type acls: - - id: item.type.menu + - id: item_type.menu label: Menu - - id: item.type.list + - id: item_type.list label: List - - id: item.type.add + - id: item_type.add label: Add - - id: item.type.update + - id: item_type.update label: Update - - id: item.type.delete + - id: item_type.delete label: Delete - id: item diff --git a/config/packages/catalyst_menu.yaml b/config/packages/catalyst_menu.yaml index a92f2c4e..1c4f7417 100644 --- a/config/packages/catalyst_menu.yaml +++ b/config/packages/catalyst_menu.yaml @@ -288,6 +288,10 @@ catalyst_menu: acl: service_offering.menu label: '[menu.database.serviceofferings]' parent: database + - id: item_type_list + acl: item_type.menu + label: '[menu.database.itemtypes]' + parent: database - id: item acl: item.menu diff --git a/config/routes/item_type.yaml b/config/routes/item_type.yaml new file mode 100644 index 00000000..adaa8dee --- /dev/null +++ b/config/routes/item_type.yaml @@ -0,0 +1,34 @@ +item_type_list: + path: /item-types + controller: App\Controller\ItemTypeController::index + methods: [GET] + +item_type_rows: + path: /item-types/rowdata + controller: App\Controller\ItemTypeController::datatableRows + methods: [POST] + +item_type_add_form: + path: /item-types/newform + controller: App\Controller\ItemTypeController::addForm + methods: [GET] + +item_type_add_submit: + path: /item-types + controller: App\Controller\ItemTypeController::addSubmit + methods: [POST] + +item_type_update_form: + path: /item-types/{id} + controller: App\Controller\ItemTypeController::updateForm + methods: [GET] + +item_type_update_submit: + path: /item-types/{id} + controller: App\Controller\ItemTypeController::updateSubmit + methods: [POST] + +item_type_delete: + path: /item-types/{id} + controller: App\Controller\ItemTypeController::deleteSubmit + methods: [DELETE] diff --git a/config/routes/price_tier.yaml b/config/routes/price_tier.yaml index f0127e12..397858d9 100644 --- a/config/routes/price_tier.yaml +++ b/config/routes/price_tier.yaml @@ -1,34 +1,34 @@ price_tier_list: - path: /pricetiers + path: /price-tiers controller: App\Controller\PriceTierController::index methods: [GET] price_tier_rows: - path: /pricetiers/rows + path: /price-tiers/rows controller: App\Controller\PriceTierController::datatableRows methods: [POST] price_tier_add_form: - path: /pricetiers/newform + path: /price-tiers/newform controller: App\Controller\PriceTierController::addForm methods: [GET] price_tier_add_submit: - path: /pricetiers + path: /price-tiers controller: App\Controller\PriceTierController::addSubmit methods: [POST] price_tier_update_form: - path: /pricetiers/{id} + path: /price-tiers/{id} controller: App\Controller\PriceTierController::updateForm methods: [GET] price_tier_update_submit: - path: /pricetiers/{id} + path: /price-tiers/{id} controller: App\Controller\PriceTierController::updateSubmit methods: [POST] price_tier_delete: - path: /pricetiers/{id} + path: /price-tiers/{id} controller: App\Controller\PriceTierController::deleteSubmit methods: [DELETE] diff --git a/src/Controller/ItemTypeController.php b/src/Controller/ItemTypeController.php new file mode 100644 index 00000000..c7c1b110 --- /dev/null +++ b/src/Controller/ItemTypeController.php @@ -0,0 +1,251 @@ +render('item-type/list.html.twig'); + } + + /** + * @IsGranted("item_type.list") + */ + public function datatableRows(Request $req) + { + // get query builder + $qb = $this->getDoctrine() + ->getRepository(ItemType::class) + ->createQueryBuilder('q'); + + // get datatable params + $datatable = $req->request->get('datatable'); + + // count total records + $tquery = $qb->select('COUNT(q)'); + $this->setQueryFilters($datatable, $tquery); + $total = $tquery->getQuery() + ->getSingleScalarResult(); + + // get current page number + $page = $datatable['pagination']['page'] ?? 1; + + $perpage = $datatable['pagination']['perpage']; + $offset = ($page - 1) * $perpage; + + // add metadata + $meta = [ + 'page' => $page, + 'perpage' => $perpage, + 'pages' => ceil($total / $perpage), + 'total' => $total, + 'sort' => 'asc', + 'field' => 'id' + ]; + + // build query + $query = $qb->select('q'); + $this->setQueryFilters($datatable, $query); + + // check if sorting is present, otherwise use default + if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) { + $order = $datatable['sort']['sort'] ?? 'asc'; + $query->orderBy('q.' . $datatable['sort']['field'], $order); + } else { + $query->orderBy('q.id', 'asc'); + } + + // get rows for this page + $obj_rows = $query->setFirstResult($offset) + ->setMaxResults($perpage) + ->getQuery() + ->getResult(); + + // process rows + $rows = []; + foreach ($obj_rows as $orow) { + // add row data + $row['id'] = $orow->getID(); + $row['name'] = $orow->getName(); + + // add row metadata + $row['meta'] = [ + 'update_url' => '', + 'delete_url' => '' + ]; + + // add crud urls + if ($this->isGranted('item_type.update')) + $row['meta']['update_url'] = $this->generateUrl('item_type_update_form', ['id' => $row['id']]); + if ($this->isGranted('item_type.delete')) + $row['meta']['delete_url'] = $this->generateUrl('item_type_delete', ['id' => $row['id']]); + + $rows[] = $row; + } + + // response + return $this->json([ + 'meta' => $meta, + 'data' => $rows + ]); + } + + /** + * @Menu(selected="item_type.list") + * @IsGranted("item_type.add") + */ + public function addForm() + { + $item_type = new ItemType(); + $params = [ + 'obj' => $item_type, + 'mode' => 'create', + ]; + + // response + return $this->render('item-type/form.html.twig', $params); + } + + /** + * @IsGranted("item_type.add") + */ + public function addSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator) + { + $item_type = new ItemType(); + + $this->setObject($item_type, $req); + + // validate + $errors = $validator->validate($item_type); + + // initialize error list + $error_array = []; + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // validated! save the entity + $em->persist($item_type); + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + /** + * @Menu(selected="item_type_list") + * @ParamConverter("item_type", class="App\Entity\ItemType") + * @IsGranted("item_type.update") + */ + public function updateForm($id, EntityManagerInterface $em, ItemType $item_type) + { + $params = []; + $params['obj'] = $item_type; + $params['mode'] = 'update'; + + // response + return $this->render('item-type/form.html.twig', $params); + } + + /** + * @ParamConverter("item_type", class="App\Entity\ItemType") + * @IsGranted("item_type.update") + */ + public function updateSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator, ItemType $item_type) + { + $this->setObject($item_type, $req); + + // validate + $errors = $validator->validate($item_type); + + // initialize error list + $error_array = []; + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // validated! save the entity + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + /** + * @ParamConverter("item_type", class="App\Entity\ItemType") + * @IsGranted("item_type.delete") + */ + public function deleteSubmit(EntityManagerInterface $em, ItemType $item_type) + { + // delete this object + $em->remove($item_type); + $em->flush(); + + // response + $response = new Response(); + $response->setStatusCode(Response::HTTP_OK); + $response->send(); + } + + + protected function setObject(ItemType $obj, Request $req) + { + // set and save values + $obj->setName($req->request->get('name')) + ->setCode($req->request->get('code')); + } + + protected function setQueryFilters($datatable, QueryBuilder $query) + { + if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) { + $query->where('q.name LIKE :filter') + ->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%'); + } + } +} diff --git a/templates/item-type/form.html.twig b/templates/item-type/form.html.twig new file mode 100644 index 00000000..97d0d9ec --- /dev/null +++ b/templates/item-type/form.html.twig @@ -0,0 +1,142 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

Item Types

+
+
+
+ +
+ +
+
+
+
+
+
+ + + +

+ {% if mode == 'update' %} + Edit Item Type + {{ obj.getName() }} + {% else %} + New Item Type + {% endif %} +

+
+
+
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Back +
+
+
+
+
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/templates/item-type/list.html.twig b/templates/item-type/list.html.twig new file mode 100644 index 00000000..cc11e81d --- /dev/null +++ b/templates/item-type/list.html.twig @@ -0,0 +1,146 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

+ Item Types +

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index 7278212d..8387663e 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -159,6 +159,7 @@ menu.database.subtickettypes: 'Sub Ticket Types' menu.database.emergencytypes: 'Emergency Types' menu.database.ownershiptypes: 'Ownership Types' menu.database.serviceofferings: 'Service Offerings' +menu.database.itemtypes: 'Item Types' # fcm jo status updates jo_fcm_title_outlet_assign: 'Looking for riders' From e4ffcc0c9dcd199bcb46c0a983fdc40a7fe7c136 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Wed, 27 Dec 2023 22:15:44 -0500 Subject: [PATCH 05/29] Add controller for item pricing. #780 --- config/packages/catalyst_auth.yaml | 5 + config/packages/catalyst_menu.yaml | 4 + config/routes/item_pricing.yaml | 14 +++ src/Controller/ItemPricingController.php | 76 +++++++++++++ templates/item-pricing/form.html.twig | 129 +++++++++++++++++++++++ 5 files changed, 228 insertions(+) create mode 100644 config/routes/item_pricing.yaml create mode 100644 src/Controller/ItemPricingController.php create mode 100644 templates/item-pricing/form.html.twig diff --git a/config/packages/catalyst_auth.yaml b/config/packages/catalyst_auth.yaml index f63c5bc0..7e02d5c0 100644 --- a/config/packages/catalyst_auth.yaml +++ b/config/packages/catalyst_auth.yaml @@ -675,6 +675,11 @@ catalyst_auth: label: Update - id: item.delete label: Delete + - id: item_pricing + label: Item Pricing + acls: + - id: item_pricing.update + label: Update api: user_entity: "App\\Entity\\ApiUser" diff --git a/config/packages/catalyst_menu.yaml b/config/packages/catalyst_menu.yaml index 1c4f7417..c861471d 100644 --- a/config/packages/catalyst_menu.yaml +++ b/config/packages/catalyst_menu.yaml @@ -302,6 +302,10 @@ catalyst_menu: acl: price_tier.list label: Price Tiers parent: item + - id: item_pricing + acl: item_pricing.update + label: Item Pricing + parent: item - id: item_list acl: item.list label: Items diff --git a/config/routes/item_pricing.yaml b/config/routes/item_pricing.yaml new file mode 100644 index 00000000..df289902 --- /dev/null +++ b/config/routes/item_pricing.yaml @@ -0,0 +1,14 @@ +item_pricing: + path: /item-pricing + controller: App\Controller\ItemPricingController::index + methods: [GET] + +item_pricing_update: + path: /item-pricing + controller: App\Controller\ItemPricingController::formSubmit + methods: [POST] + +item_pricing_prices: + path: /item-pricing/{id}/prices + controller: App\Controller\ItemPricingController::itemPrices + methods: [GET] diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php new file mode 100644 index 00000000..9aba9f68 --- /dev/null +++ b/src/Controller/ItemPricingController.php @@ -0,0 +1,76 @@ +getRepository(PriceTier::class)->findAll(); + + // get all the items/batteries + $items = $em->getRepository(Battery::class)->findBy(['flag_active' => true]); + + $params = [ + 'sets' => [ + 'price_tiers' => $price_tiers + ], + 'items' => $items, + ]; + + return $this->render('item-pricing/form.html.twig', $params); + } + + /** + * @Menu(selected="item_pricing") + * @IsGranted("item_pricing.update") + */ + public function formSubmit(Request $req, EntityManagerInterface $em) + { + } + + /** + * @IsGranted("item_pricing.update") + */ + public function (EntityManagerInterface $em, $id) + { + $pt_prices = []; + // check if default prices are needed + if ($id != 0) + { + // get the price tier prices + } + else + { + // get the prices from battery + } + + $data_items = []; + + // response + return new JsonResponse([ + 'items' => $data_items, + ]); + } + diff --git a/templates/item-pricing/form.html.twig b/templates/item-pricing/form.html.twig new file mode 100644 index 00000000..cde71ae6 --- /dev/null +++ b/templates/item-pricing/form.html.twig @@ -0,0 +1,129 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

Item Pricing

+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + {% for item in items %} + + + + + + {% endfor %} + +
IDNamePrice
{{ item.getID }}{{ item.getModel.getName ~ ' ' ~ item.getSize.getName}} + +
+
+ +
+
+
+
+
+
+
+
+{% endblock %} + +{% block js_end %} + +{% endblock %} From 01e4baa8c4473775899d78c43155ffad6f155fa6 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Thu, 28 Dec 2023 20:41:44 -0500 Subject: [PATCH 06/29] Add item price controller. #780 --- config/packages/catalyst_auth.yaml | 14 +-- config/packages/catalyst_menu.yaml | 4 +- config/routes/item_price.yaml | 34 ++++++ src/Controller/ItemPriceController.php | 126 +++++++++++++++++++++++ src/Controller/ItemPricingController.php | 13 ++- 5 files changed, 179 insertions(+), 12 deletions(-) create mode 100644 config/routes/item_price.yaml create mode 100644 src/Controller/ItemPriceController.php diff --git a/config/packages/catalyst_auth.yaml b/config/packages/catalyst_auth.yaml index 7e02d5c0..e03cdc32 100644 --- a/config/packages/catalyst_auth.yaml +++ b/config/packages/catalyst_auth.yaml @@ -662,18 +662,18 @@ catalyst_auth: - id: item_type.delete label: Delete - - id: item - label: Item + - id: item_price + label: Item Price acls: - - id: item.menu + - id: item_price.menu label: Menu - - id: item.list + - id: item_price.list label: List - - id: item.add + - id: item_price.add label: Add - - id: item.update + - id: item_price.update label: Update - - id: item.delete + - id: item_price.delete label: Delete - id: item_pricing label: Item Pricing diff --git a/config/packages/catalyst_menu.yaml b/config/packages/catalyst_menu.yaml index c861471d..83f7e852 100644 --- a/config/packages/catalyst_menu.yaml +++ b/config/packages/catalyst_menu.yaml @@ -306,7 +306,7 @@ catalyst_menu: acl: item_pricing.update label: Item Pricing parent: item - - id: item_list - acl: item.list + - id: item_price_list + acl: item_price.list label: Items parent: item diff --git a/config/routes/item_price.yaml b/config/routes/item_price.yaml new file mode 100644 index 00000000..607dc3cd --- /dev/null +++ b/config/routes/item_price.yaml @@ -0,0 +1,34 @@ +item_price_list: + path: /items + controller: App\Controller\ItemPriceController::index + methods: [GET] + +item_price_rows: + path: /items/rowdata + controller: App\Controller\ItemPriceController::datatableRows + methods: [POST] + +item_price_add_form: + path: /items/newform + controller: App\Controller\ItemPriceController::addForm + methods: [GET] + +item_price_add_submit: + path: /items + controller: App\Controller\ItemPriceController::addSubmit + methods: [POST] + +item_price_update_form: + path: /items/{id} + controller: App\Controller\ItemPriceController::updateForm + methods: [GET] + +item_price_update_submit: + path: /items/{id} + controller: App\Controller\ItemPriceController::updateSubmit + methods: [POST] + +item_price_delete: + path: /items/{id} + controller: App\Controller\ItemPriceController::deleteSubmit + methods: [DELETE] diff --git a/src/Controller/ItemPriceController.php b/src/Controller/ItemPriceController.php new file mode 100644 index 00000000..0a41994c --- /dev/null +++ b/src/Controller/ItemPriceController.php @@ -0,0 +1,126 @@ +render('item-price/list.html.twig'); + } + + /** + * @IsGranted("item_price.list") + */ + public function datatableRows(Request $req) + { + // get query builder + $qb = $this->getDoctrine() + ->getRepository(ItemPrice::class) + ->createQueryBuilder('q'); + + // get datatable params + $datatable = $req->request->get('datatable'); + + // count total records + $tquery = $qb->select('COUNT(q)') + ->innerJoin('q.battery', 'b', 'WITH', 'b.id = q.item_id') + ->innerJoin('q.service_offering', 'so', 'WITH', 'so.id = q.item_id'); + $this->setQueryFilters($datatable, $tquery); + $total = $tquery->getQuery() + ->getSingleScalarResult(); + + // get current page number + $page = $datatable['pagination']['page'] ?? 1; + + $perpage = $datatable['pagination']['perpage']; + $offset = ($page - 1) * $perpage; + + // add metadata + $meta = [ + 'page' => $page, + 'perpage' => $perpage, + 'pages' => ceil($total / $perpage), + 'total' => $total, + 'sort' => 'asc', + 'field' => 'id' + ]; + + // build query + $query = $qb->select('q') + ->innerJoin('q.battery', 'b', 'WITH', 'b.id = q.item_id') + ->innerJoin('q.service_offering', 'so', 'WITH', 'so.id = q.item_id'); + $this->setQueryFilters($datatable, $query); + + // check if sorting is present, otherwise use default + if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) { + $order = $datatable['sort']['sort'] ?? 'asc'; + $query->orderBy('q.' . $datatable['sort']['field'], $order); + } else { + $query->orderBy('q.id', 'asc'); + } + + // get rows for this page + $obj_rows = $query->setFirstResult($offset) + ->setMaxResults($perpage) + ->getQuery() + ->getResult(); + + // process rows + $rows = []; + foreach ($obj_rows as $orow) { + // add row data + $row['id'] = $orow->getID(); + + // add row metadata + $row['meta'] = [ + 'update_url' => '', + 'delete_url' => '' + ]; + + // add crud urls + if ($this->isGranted('item_price.update')) + $row['meta']['update_url'] = $this->generateUrl('item_price_update_form', ['id' => $row['id']]); + if ($this->isGranted('item_price.delete')) + $row['meta']['delete_url'] = $this->generateUrl('item_price_delete', ['id' => $row['id']]); + + $rows[] = $row; + } + + // response + return $this->json([ + 'meta' => $meta, + 'data' => $rows + ]); + } + + protected function setQueryFilters($datatable, QueryBuilder $query) + { + if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) { + $query->where('q.name LIKE :filter') + ->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%'); + } + } +} diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php index 9aba9f68..ddfd60bf 100644 --- a/src/Controller/ItemPricingController.php +++ b/src/Controller/ItemPricingController.php @@ -53,13 +53,20 @@ class ItemPricingController extends Controller /** * @IsGranted("item_pricing.update") */ - public function (EntityManagerInterface $em, $id) + public function itemPrices(EntityManagerInterface $em, $id) { $pt_prices = []; // check if default prices are needed if ($id != 0) { - // get the price tier prices + // get the price tier + $pt = $em->getRepository(PriceTier::class)->find($id); + + // get the item prices under the price tier + $pt_item_prices = $pt->getItemPrices(); + foreach ($pt_item_prices as $pt_item_price) + { + } } else { @@ -73,4 +80,4 @@ class ItemPricingController extends Controller 'items' => $data_items, ]); } - +} From 80b9f903247d4d05ccc80161ecbe9e6f0f2e63b6 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Sat, 30 Dec 2023 23:09:27 -0500 Subject: [PATCH 07/29] Rename ItemPriceController to ItemController. #780 --- config/packages/catalyst_auth.yaml | 14 +- config/packages/catalyst_menu.yaml | 6 +- config/routes/item.yaml | 34 ++++ config/routes/item_price.yaml | 34 ---- ...PriceController.php => ItemController.php} | 16 +- src/Entity/{ItemPrice.php => Item.php} | 19 ++- src/Entity/ItemType.php | 11 ++ src/Entity/PriceTier.php | 10 +- templates/item/form.html.twig | 142 +++++++++++++++++ templates/item/list.html.twig | 146 ++++++++++++++++++ templates/price-tier/form.html.twig | 2 +- 11 files changed, 373 insertions(+), 61 deletions(-) create mode 100644 config/routes/item.yaml delete mode 100644 config/routes/item_price.yaml rename src/Controller/{ItemPriceController.php => ItemController.php} (90%) rename src/Entity/{ItemPrice.php => Item.php} (83%) create mode 100644 templates/item/form.html.twig create mode 100644 templates/item/list.html.twig diff --git a/config/packages/catalyst_auth.yaml b/config/packages/catalyst_auth.yaml index e03cdc32..7e02d5c0 100644 --- a/config/packages/catalyst_auth.yaml +++ b/config/packages/catalyst_auth.yaml @@ -662,18 +662,18 @@ catalyst_auth: - id: item_type.delete label: Delete - - id: item_price - label: Item Price + - id: item + label: Item acls: - - id: item_price.menu + - id: item.menu label: Menu - - id: item_price.list + - id: item.list label: List - - id: item_price.add + - id: item.add label: Add - - id: item_price.update + - id: item.update label: Update - - id: item_price.delete + - id: item.delete label: Delete - id: item_pricing label: Item Pricing diff --git a/config/packages/catalyst_menu.yaml b/config/packages/catalyst_menu.yaml index 83f7e852..a6bd1583 100644 --- a/config/packages/catalyst_menu.yaml +++ b/config/packages/catalyst_menu.yaml @@ -297,7 +297,7 @@ catalyst_menu: acl: item.menu label: Item Management icon: fa fa-boxes - order: 10 + order: 10 - id: price_tier_list acl: price_tier.list label: Price Tiers @@ -306,7 +306,7 @@ catalyst_menu: acl: item_pricing.update label: Item Pricing parent: item - - id: item_price_list - acl: item_price.list + - id: item_list + acl: item.list label: Items parent: item diff --git a/config/routes/item.yaml b/config/routes/item.yaml new file mode 100644 index 00000000..89dde4d2 --- /dev/null +++ b/config/routes/item.yaml @@ -0,0 +1,34 @@ +item_list: + path: /items + controller: App\Controller\ItemController::index + methods: [GET] + +item_rows: + path: /items/rowdata + controller: App\Controller\ItemController::datatableRows + methods: [POST] + +item_add_form: + path: /items/newform + controller: App\Controller\ItemController::addForm + methods: [GET] + +item_add_submit: + path: /items + controller: App\Controller\ItemController::addSubmit + methods: [POST] + +item_update_form: + path: /items/{id} + controller: App\Controller\ItemController::updateForm + methods: [GET] + +item_update_submit: + path: /items/{id} + controller: App\Controller\ItemController::updateSubmit + methods: [POST] + +item_delete: + path: /items/{id} + controller: App\Controller\ItemController::deleteSubmit + methods: [DELETE] diff --git a/config/routes/item_price.yaml b/config/routes/item_price.yaml deleted file mode 100644 index 607dc3cd..00000000 --- a/config/routes/item_price.yaml +++ /dev/null @@ -1,34 +0,0 @@ -item_price_list: - path: /items - controller: App\Controller\ItemPriceController::index - methods: [GET] - -item_price_rows: - path: /items/rowdata - controller: App\Controller\ItemPriceController::datatableRows - methods: [POST] - -item_price_add_form: - path: /items/newform - controller: App\Controller\ItemPriceController::addForm - methods: [GET] - -item_price_add_submit: - path: /items - controller: App\Controller\ItemPriceController::addSubmit - methods: [POST] - -item_price_update_form: - path: /items/{id} - controller: App\Controller\ItemPriceController::updateForm - methods: [GET] - -item_price_update_submit: - path: /items/{id} - controller: App\Controller\ItemPriceController::updateSubmit - methods: [POST] - -item_price_delete: - path: /items/{id} - controller: App\Controller\ItemPriceController::deleteSubmit - methods: [DELETE] diff --git a/src/Controller/ItemPriceController.php b/src/Controller/ItemController.php similarity index 90% rename from src/Controller/ItemPriceController.php rename to src/Controller/ItemController.php index 0a41994c..07e66df1 100644 --- a/src/Controller/ItemPriceController.php +++ b/src/Controller/ItemController.php @@ -14,31 +14,31 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use App\Entity\ItemType; -use App\Entity\ItemPrice; +use App\Entity\Item; use App\Entity\Battery; use App\Entity\ServiceOffering; use Catalyst\MenuBundle\Annotation\Menu; -class ItemPriceController extends Controller +class ItemController extends Controller { /** - * @Menu(selected="item_price_list") - * @IsGranted("item_price.list") + * @Menu(selected="item_list") + * @IsGranted("item.list") */ public function index () { - return $this->render('item-price/list.html.twig'); + return $this->render('item/list.html.twig'); } /** - * @IsGranted("item_price.list") + * @IsGranted("item.list") */ public function datatableRows(Request $req) { // get query builder $qb = $this->getDoctrine() - ->getRepository(ItemPrice::class) + ->getRepository(Item::class) ->createQueryBuilder('q'); // get datatable params @@ -118,6 +118,8 @@ class ItemPriceController extends Controller protected function setQueryFilters($datatable, QueryBuilder $query) { + // TODO: add filter for item type. + // TODO: fix filter for name since name is with the associated entity if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) { $query->where('q.name LIKE :filter') ->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%'); diff --git a/src/Entity/ItemPrice.php b/src/Entity/Item.php similarity index 83% rename from src/Entity/ItemPrice.php rename to src/Entity/Item.php index b9f14517..2e5ed091 100644 --- a/src/Entity/ItemPrice.php +++ b/src/Entity/Item.php @@ -6,10 +6,10 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity - * @ORM\Table(name="item_price") + * @ORM\Table(name="item") */ -class ItemPrice +class Item { // unique id /** @@ -20,14 +20,14 @@ class ItemPrice protected $id; /** - * @ORM\ManyToOne(targetEntity="PriceTier", inversedBy="item_prices") + * @ORM\ManyToOne(targetEntity="PriceTier", inversedBy="items") * @ORM\JoinColumn(name="price_tier_id", referencedColumnName="id") */ protected $price_tier; // item type /** - * @ORM\ManyToOne(targetEntity="ItemType", inversedBy="item_prices") + * @ORM\ManyToOne(targetEntity="ItemType", inversedBy="items") * @ORM\JoinColumn(name="item_type_id", referencedColumnName="id") */ protected $item_type; @@ -62,6 +62,17 @@ class ItemPrice return $this->price_tier; } + public function setItemType(ItemType $item_type) + { + $this->item_type = $item_type; + return $this; + } + + public function getItemType() + { + return $this->item_type; + } + public function setItemID($item_id) { $this->item_id = $item_id; diff --git a/src/Entity/ItemType.php b/src/Entity/ItemType.php index 204fb9b3..3ef422d8 100644 --- a/src/Entity/ItemType.php +++ b/src/Entity/ItemType.php @@ -35,6 +35,12 @@ class ItemType */ protected $code; + // items under an item type + /** + * @ORM\OneToMany(targetEntity="Item", mappedBy="item_type") + */ + protected $items; + public function __construct() { $this->code = ''; @@ -66,4 +72,9 @@ class ItemType { return $this->code; } + + public function getItems() + { + return $this->items; + } } diff --git a/src/Entity/PriceTier.php b/src/Entity/PriceTier.php index 37c8d8f8..d929fc8f 100644 --- a/src/Entity/PriceTier.php +++ b/src/Entity/PriceTier.php @@ -34,14 +34,14 @@ class PriceTier // items under a price tier /** - * @ORM\OneToMany(targetEntity="ItemPrice", mappedBy="price_tier") + * @ORM\OneToMany(targetEntity="Item", mappedBy="price_tier") */ - protected $item_prices; + protected $items; public function __construct() { $this->supported_areas = new ArrayCollection(); - $this->item_prices = new ArrayCollection(); + $this->items = new ArrayCollection(); } public function getID() @@ -80,9 +80,9 @@ class PriceTier return $this; } - public function getItemPrices() + public function getItems() { - return $this->item_prices; + return $this->items; } } diff --git a/templates/item/form.html.twig b/templates/item/form.html.twig new file mode 100644 index 00000000..97d0d9ec --- /dev/null +++ b/templates/item/form.html.twig @@ -0,0 +1,142 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

Item Types

+
+
+
+ +
+ +
+
+
+
+
+
+ + + +

+ {% if mode == 'update' %} + Edit Item Type + {{ obj.getName() }} + {% else %} + New Item Type + {% endif %} +

+
+
+
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Back +
+
+
+
+
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/templates/item/list.html.twig b/templates/item/list.html.twig new file mode 100644 index 00000000..b3a36a61 --- /dev/null +++ b/templates/item/list.html.twig @@ -0,0 +1,146 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

+ Items +

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/templates/price-tier/form.html.twig b/templates/price-tier/form.html.twig index 105d7ec2..0056cdc8 100644 --- a/templates/price-tier/form.html.twig +++ b/templates/price-tier/form.html.twig @@ -49,7 +49,7 @@
{% if sets.areas is empty %} - No supported areas. + No available supported areas. {% else %}
{% for id, label in sets.areas %} From c17be92f0a9d24c7e5541345a9f7e6f659d26996 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Sun, 31 Dec 2023 00:46:12 -0500 Subject: [PATCH 08/29] Fix errors for item query. #780 --- src/Controller/ItemController.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Controller/ItemController.php b/src/Controller/ItemController.php index 07e66df1..88cc769c 100644 --- a/src/Controller/ItemController.php +++ b/src/Controller/ItemController.php @@ -5,6 +5,7 @@ namespace App\Controller; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Query\Expr\Join; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Validator\Validator\ValidatorInterface; @@ -37,7 +38,7 @@ class ItemController extends Controller public function datatableRows(Request $req) { // get query builder - $qb = $this->getDoctrine() + $total_qb = $this->getDoctrine() ->getRepository(Item::class) ->createQueryBuilder('q'); @@ -45,9 +46,9 @@ class ItemController extends Controller $datatable = $req->request->get('datatable'); // count total records - $tquery = $qb->select('COUNT(q)') - ->innerJoin('q.battery', 'b', 'WITH', 'b.id = q.item_id') - ->innerJoin('q.service_offering', 'so', 'WITH', 'so.id = q.item_id'); + $tquery = $total_qb->select('COUNT(q)') + ->leftJoin(Battery::class, 'battery', Join::WITH, 'battery.id = q.item_id') + ->leftJoin(ServiceOffering::class, 'so', Join::WITH, 'so.id = q.item_id'); $this->setQueryFilters($datatable, $tquery); $total = $tquery->getQuery() ->getSingleScalarResult(); @@ -68,10 +69,15 @@ class ItemController extends Controller 'field' => 'id' ]; + // reset query builder + $qb = $this->getDoctrine() + ->getRepository(Item::class) + ->createQueryBuilder('q'); + // build query $query = $qb->select('q') - ->innerJoin('q.battery', 'b', 'WITH', 'b.id = q.item_id') - ->innerJoin('q.service_offering', 'so', 'WITH', 'so.id = q.item_id'); + ->leftJoin(Battery::class, 'battery', Join::WITH, 'battery.id = q.item_id') + ->leftJoin(ServiceOffering::class, 'so', Join::WITH, 'so.id = q.item_id'); $this->setQueryFilters($datatable, $query); // check if sorting is present, otherwise use default From f65ca190107f7bb70052cdce0ae65671257d2e6d Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Tue, 9 Jan 2024 17:39:29 +0800 Subject: [PATCH 09/29] Load service offering into Item Pricing page. #780 --- config/routes/item_pricing.yaml | 2 +- src/Controller/ItemController.php | 54 ++++++++++++++++++++ src/Controller/ItemPricingController.php | 57 ++++++++++++++++++--- templates/item-pricing/form.html.twig | 37 ++++++++++++-- templates/item/form.html.twig | 65 ++++++++++++++++++------ 5 files changed, 188 insertions(+), 27 deletions(-) diff --git a/config/routes/item_pricing.yaml b/config/routes/item_pricing.yaml index df289902..a557a1ec 100644 --- a/config/routes/item_pricing.yaml +++ b/config/routes/item_pricing.yaml @@ -9,6 +9,6 @@ item_pricing_update: methods: [POST] item_pricing_prices: - path: /item-pricing/{id}/prices + path: /item-pricing/{pt_id}/{it_id}/prices controller: App\Controller\ItemPricingController::itemPrices methods: [GET] diff --git a/src/Controller/ItemController.php b/src/Controller/ItemController.php index 88cc769c..7e9cd277 100644 --- a/src/Controller/ItemController.php +++ b/src/Controller/ItemController.php @@ -122,6 +122,60 @@ class ItemController extends Controller ]); } + /** + * @Menu(selected="item.list") + * @IsGranted("item.add") + */ + public function addForm(EntityManagerInterface $em) + { + $item = new Item(); + + // get the sets for the dropdowns + $sets = $this->generateFormSets($em); + + $params = [ + 'obj' => $item, + 'sets' => $sets, + 'mode' => 'create', + ]; + + // response + return $this->render('item/form.html.twig', $params); + } + + protected function generateFormSets(EntityManagerInterface $em) + { + // item types + $item_types = $em->getRepository(ItemType::class)->findby([], ['name' => 'asc']); + $item_type_set = []; + foreach ($item_types as $it) + { + $item_type_set[$it->getID()] = $it->getName(); + } + + // batteries + $batts = $em->getRepository(Battery::class)->findAll(); + $batt_set = []; + foreach ($batts as $batt) + { + $batt_set[$batt->getID()] = $batt->getModel()->getName() . ' ' . $batt->getSize()->getName(); + } + + // service offerings + $services = $em->getRepository(ServiceOffering::class)->findBy([],['name' => 'asc']); + $service_set = []; + foreach ($services as $service) + { + $service_set[$service->getID()] = $service->getName(); + } + + return [ + 'item_types' => $item_type_set, + 'batteries' => $batt_set, + 'services' => $service_set, + ]; + } + protected function setQueryFilters($datatable, QueryBuilder $query) { // TODO: add filter for item type. diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php index ddfd60bf..537ae2a6 100644 --- a/src/Controller/ItemPricingController.php +++ b/src/Controller/ItemPricingController.php @@ -17,6 +17,8 @@ use Catalyst\MenuBundle\Annotation\Menu; use App\Entity\PriceTier; use App\Entity\Battery; +use App\Entity\ServiceOffering; +use App\Entity\ItemType; class ItemPricingController extends Controller { @@ -29,12 +31,16 @@ class ItemPricingController extends Controller // get all the price tiers $price_tiers = $em->getRepository(PriceTier::class)->findAll(); + // get all item types + $item_types = $em->getRepository(ItemType::class)->findBy([], ['name' => 'asc']); + // get all the items/batteries - $items = $em->getRepository(Battery::class)->findBy(['flag_active' => true]); + $items = $this->getAllItems($em); $params = [ 'sets' => [ - 'price_tiers' => $price_tiers + 'price_tiers' => $price_tiers, + 'item_types' => $item_types, ], 'items' => $items, ]; @@ -53,7 +59,7 @@ class ItemPricingController extends Controller /** * @IsGranted("item_pricing.update") */ - public function itemPrices(EntityManagerInterface $em, $id) + public function itemPrices(EntityManagerInterface $em, $pt_id, $it_id) { $pt_prices = []; // check if default prices are needed @@ -62,9 +68,9 @@ class ItemPricingController extends Controller // get the price tier $pt = $em->getRepository(PriceTier::class)->find($id); - // get the item prices under the price tier - $pt_item_prices = $pt->getItemPrices(); - foreach ($pt_item_prices as $pt_item_price) + // get the items under the price tier + $pt_items = $pt->getItems(); + foreach ($pt_items as $pt_item) { } } @@ -80,4 +86,43 @@ class ItemPricingController extends Controller 'items' => $data_items, ]); } + + protected function getAllItems(EntityManagerInterface $em) + { + // get the item type for battery + $batt_item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']); + + // get the item type for service offering + $service_item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + + // get all active batteries + $batts = $em->getRepository(Battery::class)->findBy(['flag_active' => true]); + foreach ($batts as $batt) + { + $batt_set[$batt->getID()] = [ + 'name' => $batt->getModel()->getName() . ' ' . $batt->getSize()->getName(), + 'item_type_id' => $batt_item_type->getID(), + 'item_type' => $batt_item_type->getName(), + 'price' => $batt->getSellingPrice(), + ]; + } + + // get all service offerings + $services = $em->getRepository(ServiceOffering::class)->findBy([], ['name' => 'asc']); + $service_set = []; + foreach ($services as $service) + { + $service_set[$service->getID()] = [ + 'name' => $service->getName(), + 'item_type_id' => $service_item_type->getID(), + 'item_type' => $service_item_type->getName(), + 'price' => $service->getFee(), + ]; + } + + return [ + 'batteries' => $batt_set, + 'services' => $service_set, + ]; + } } diff --git a/templates/item-pricing/form.html.twig b/templates/item-pricing/form.html.twig index cde71ae6..d9387dac 100644 --- a/templates/item-pricing/form.html.twig +++ b/templates/item-pricing/form.html.twig @@ -34,6 +34,17 @@
+
+
+
+ +
+
+
@@ -46,19 +57,34 @@ ID Name + Item Type ID + Item Type Price - {% for item in items %} + {% for id, item in items.batteries %} - {{ item.getID }} - {{ item.getModel.getName ~ ' ' ~ item.getSize.getName}} + {{ id }} + {{ item.name }} + {{ item.item_type_id }} + {{ item.item_type }} - + - {% endfor %} + {% endfor %} + {% for id, item in items.services %} + + {{ id }} + {{ item.name}} + {{ item.item_type_id }} + {{ item.item_type}} + + + + + {% endfor %}
@@ -116,6 +142,7 @@ function update_table(data) { item_html += ''; item_html += '' + item.id + ''; item_html += '' + item.name + ''; + item_html += '' + item.item_type_id + ''; item_html += ''; item_html += ''; item_html += ''; diff --git a/templates/item/form.html.twig b/templates/item/form.html.twig index 97d0d9ec..f7a76ce1 100644 --- a/templates/item/form.html.twig +++ b/templates/item/form.html.twig @@ -5,7 +5,7 @@
-

Item Types

+

Items

@@ -23,33 +23,53 @@

{% if mode == 'update' %} - Edit Item Type + Edit Item {{ obj.getName() }} {% else %} - New Item Type + New Item {% endif %}

-
+
-
-
-
@@ -58,7 +78,7 @@
- Back + Back
@@ -90,7 +110,7 @@ $(function() { text: 'Your changes have been saved!', type: 'success', onClose: function() { - window.location.href = "{{ url('item_type_list') }}"; + window.location.href = "{{ url('item_list') }}"; } }); }).fail(function(response) { @@ -138,5 +158,20 @@ $(function() { $(".form-control-feedback[data-field]").addClass('hide'); } }); + +$('#item-type').change(function(e) { + console.log('item type change ' + e.target.value); + if (e.target.value === '1') { + // display battery row + $('#battery-row').removeClass("hide"); + + // hide service offering rows + } else { + // display service offering row + + // hide battery row + $('#battery-row').addClass("hide"); + } +}) {% endblock %} From 7f4675a8a23d73eeefc90073cac6dcd7d6b0bb5d Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Wed, 10 Jan 2024 17:23:15 +0800 Subject: [PATCH 10/29] Add item type dropdown to form. #780 --- src/Controller/ItemPricingController.php | 44 ++++++++++++++----- templates/item-pricing/form.html.twig | 55 +++++++++++++----------- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php index 537ae2a6..158d3787 100644 --- a/src/Controller/ItemPricingController.php +++ b/src/Controller/ItemPricingController.php @@ -7,6 +7,7 @@ use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Bundle\FrameworkBundle\Controller\Controller; @@ -35,7 +36,8 @@ class ItemPricingController extends Controller $item_types = $em->getRepository(ItemType::class)->findBy([], ['name' => 'asc']); // get all the items/batteries - $items = $this->getAllItems($em); + // load only batteries upon initial loading + $items = $this->getBatteries($em); $params = [ 'sets' => [ @@ -62,21 +64,37 @@ class ItemPricingController extends Controller public function itemPrices(EntityManagerInterface $em, $pt_id, $it_id) { $pt_prices = []; + + // get the item type + $it = $em->getRepository(ItemType::class)->find($it_id); + // check if default prices are needed - if ($id != 0) + if ($pt_id != 0) { // get the price tier - $pt = $em->getRepository(PriceTier::class)->find($id); + $pt = $em->getRepository(PriceTier::class)->find($pt_id); // get the items under the price tier $pt_items = $pt->getItems(); foreach ($pt_items as $pt_item) { + // make item price hash + $pt_prices[$pt_item->->getID()] = $pt_item->getPrice(); } } else { - // get the prices from battery + // get the prices from battery or service offering, depending on item type + if ($it->getCode() == 'battery') + { + // get batteries + $items = $em->getRepository(Battery::class)->findBy(['flag_active' => true]); + } + else + { + // get service offerings + $items = $em->getRepository(ServiceOffering::class)->findBy([], ['name' => 'asc']); + } } $data_items = []; @@ -87,14 +105,11 @@ class ItemPricingController extends Controller ]); } - protected function getAllItems(EntityManagerInterface $em) + protected function getBatteries(EntityManagerInterface $em) { // get the item type for battery $batt_item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']); - // get the item type for service offering - $service_item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); - // get all active batteries $batts = $em->getRepository(Battery::class)->findBy(['flag_active' => true]); foreach ($batts as $batt) @@ -107,6 +122,16 @@ class ItemPricingController extends Controller ]; } + return [ + 'items' => $batt_set, + ]; + } + + protected function getServiceOfferings(EntityeManagerInterface $em) + { + // get the item type for service offering + $service_item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + // get all service offerings $services = $em->getRepository(ServiceOffering::class)->findBy([], ['name' => 'asc']); $service_set = []; @@ -121,8 +146,7 @@ class ItemPricingController extends Controller } return [ - 'batteries' => $batt_set, - 'services' => $service_set, + 'items' => $service_set, ]; } } diff --git a/templates/item-pricing/form.html.twig b/templates/item-pricing/form.html.twig index d9387dac..ed41d9e3 100644 --- a/templates/item-pricing/form.html.twig +++ b/templates/item-pricing/form.html.twig @@ -25,8 +25,8 @@
- + {% for price_tier in sets.price_tiers %} {% endfor %} @@ -37,7 +37,7 @@
- {% for item_type in sets.item_types %} {% endfor %} @@ -63,7 +63,7 @@ - {% for id, item in items.batteries %} + {% for id, item in items.items %} {{ id }} {{ item.name }} @@ -74,17 +74,6 @@ {% endfor %} - {% for id, item in items.services %} - - {{ id }} - {{ item.name}} - {{ item.item_type_id }} - {{ item.item_type}} - - - - - {% endfor %}
@@ -99,21 +88,36 @@
{% endblock %} -{% block js_end %} +{% block scripts %} {% endblock %} From b59239055460f7f4db1a29ccb749688f698f5ea7 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Thu, 11 Jan 2024 16:48:23 +0800 Subject: [PATCH 11/29] Add response when displaying item prices. #780 --- src/Controller/ItemPricingController.php | 31 +++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php index 158d3787..bcecd5ea 100644 --- a/src/Controller/ItemPricingController.php +++ b/src/Controller/ItemPricingController.php @@ -79,7 +79,7 @@ class ItemPricingController extends Controller foreach ($pt_items as $pt_item) { // make item price hash - $pt_prices[$pt_item->->getID()] = $pt_item->getPrice(); + $pt_prices[$pt_item->getID()] = $pt_item->getPrice(); } } else @@ -98,6 +98,35 @@ class ItemPricingController extends Controller } $data_items = []; + foreach ($items as $item) + { + $item_id = $item->getID(); + + // get default price + if ($it->getCode() == 'battery') + $price = $item->getSellingPrice(); + else + $price = $item->getFee(); + + // check if tier has price for item + if (isset($pt_prices[$item_id])) + { + $price = $pt_prices[$item_id]; + + // actual price + $actual_price = number_format($price / 100, 2, '.', ''); + } + + $actual_price = $price; + + // TODO: recheck this + $data_items[] = [ + 'id' => $item_id, + 'name' => '', + 'item_type' => '', + 'price' = $actual_price, + ]; + } // response return new JsonResponse([ From bfe7a5fbf67c5c1271cc3bcb6c655241a884f5c3 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Mon, 15 Jan 2024 15:23:07 +0800 Subject: [PATCH 12/29] Fix display of item prices per tier. #780 --- src/Controller/ItemPricingController.php | 14 ++++++++++---- templates/item-pricing/form.html.twig | 5 +++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php index bcecd5ea..38bc0ce3 100644 --- a/src/Controller/ItemPricingController.php +++ b/src/Controller/ItemPricingController.php @@ -104,9 +104,15 @@ class ItemPricingController extends Controller // get default price if ($it->getCode() == 'battery') + { $price = $item->getSellingPrice(); + $name = $item->getModel()->getName() . ' ' . $item->getSize()->getName(); + } else + { $price = $item->getFee(); + $name = $item->getName(); + } // check if tier has price for item if (isset($pt_prices[$item_id])) @@ -119,12 +125,12 @@ class ItemPricingController extends Controller $actual_price = $price; - // TODO: recheck this $data_items[] = [ 'id' => $item_id, - 'name' => '', - 'item_type' => '', - 'price' = $actual_price, + 'name' => $name, + 'item_type_id' => $it->getID(), + 'item_type' => $it->getName(), + 'price' => $actual_price, ]; } diff --git a/templates/item-pricing/form.html.twig b/templates/item-pricing/form.html.twig index ed41d9e3..c27179a3 100644 --- a/templates/item-pricing/form.html.twig +++ b/templates/item-pricing/form.html.twig @@ -74,7 +74,7 @@ {% endfor %} - +
@@ -147,8 +147,9 @@ function update_table(data) { item_html += '' + item.id + ''; item_html += '' + item.name + ''; item_html += '' + item.item_type_id + ''; + item_html += '' + item.item_type + ''; item_html += ''; - item_html += ''; + item_html += ''; item_html += ''; item_html += ''; } From 022336ad8f778f72e2a2474f55054848abbb530d Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Mon, 15 Jan 2024 17:21:31 +0800 Subject: [PATCH 13/29] Add saving of prices. #780 --- src/Controller/ItemPricingController.php | 66 +++++++++++++++++++----- templates/item-pricing/form.html.twig | 4 +- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php index 38bc0ce3..f7fd78ec 100644 --- a/src/Controller/ItemPricingController.php +++ b/src/Controller/ItemPricingController.php @@ -56,6 +56,48 @@ class ItemPricingController extends Controller */ public function formSubmit(Request $req, EntityManagerInterface $em) { + $pt_id = $req->request->get('price_tier_id'); + $it_id = $req->request->get('item_type_id'); + $prices = $req->request->get('price'); + + // get the item type + $item_type = $em->getRepository(ItemType::class)->find($it_id); + + if ($pt_id == 0) + { + // default price tier, update battery or service offering, depending on item type + // NOTE: battery and service offering prices or fees are stored as decimal. + if ($item_type->getCode() == 'battery') + { + // get batteries + $items = $em->getRepository(Battery::class)->findBy(['flag_active' => true], ['id' => 'asc']); + } + else + { + // get service offerings + $items = $em->getRepository(ServiceOffering::class)->findBy([], ['id' => 'asc']); + } + + foreach ($items as $item) + { + $item_id = $item->getID(); + if (isset[$prices[$item_id]]) + { + // check item type + if ($item_type->getCode() == 'battery') + $item->setSellingPrice($prices[$item_id]); + else + $item->setFee($prices[$item_id]); + } + } + } + else + { + // get the price tier + $price_tier = $em->getRepository(PriceTier::class)->find($pt_id); + + // TODO: finish this + } } /** @@ -82,19 +124,17 @@ class ItemPricingController extends Controller $pt_prices[$pt_item->getID()] = $pt_item->getPrice(); } } + + // get the prices from battery or service offering, depending on item type + if ($it->getCode() == 'battery') + { + // get batteries + $items = $em->getRepository(Battery::class)->findBy(['flag_active' => true], ['id' => 'asc']); + } else { - // get the prices from battery or service offering, depending on item type - if ($it->getCode() == 'battery') - { - // get batteries - $items = $em->getRepository(Battery::class)->findBy(['flag_active' => true]); - } - else - { - // get service offerings - $items = $em->getRepository(ServiceOffering::class)->findBy([], ['name' => 'asc']); - } + // get service offerings + $items = $em->getRepository(ServiceOffering::class)->findBy([], ['id' => 'asc']); } $data_items = []; @@ -146,7 +186,7 @@ class ItemPricingController extends Controller $batt_item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']); // get all active batteries - $batts = $em->getRepository(Battery::class)->findBy(['flag_active' => true]); + $batts = $em->getRepository(Battery::class)->findBy(['flag_active' => true], ['id' => 'asc']); foreach ($batts as $batt) { $batt_set[$batt->getID()] = [ @@ -168,7 +208,7 @@ class ItemPricingController extends Controller $service_item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); // get all service offerings - $services = $em->getRepository(ServiceOffering::class)->findBy([], ['name' => 'asc']); + $services = $em->getRepository(ServiceOffering::class)->findBy([], ['id' => 'asc']); $service_set = []; foreach ($services as $service) { diff --git a/templates/item-pricing/form.html.twig b/templates/item-pricing/form.html.twig index c27179a3..0afd23e3 100644 --- a/templates/item-pricing/form.html.twig +++ b/templates/item-pricing/form.html.twig @@ -51,6 +51,7 @@
+
@@ -94,7 +95,6 @@ initialize(); function initialize() { - console.log('initialize'); init_price_tier_dropdown(); init_item_type_dropdown(); } @@ -126,6 +126,8 @@ function load_prices(price_tier_id, item_type_id) { update_table(JSON.parse(req.responseText)); var pt_field = document.getElementById('price-tier-id'); pt_field.value = price_tier_id; + var it_field = document.getElementById('item-type-id'); + it_field.value = item_type_id; } else { // console.log('could not load tier prices'); } From 6d7c8c5b532809757bab2f89c06daee791724683 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Tue, 16 Jan 2024 16:28:26 +0800 Subject: [PATCH 14/29] Add saving of item prices for price tier. #780 --- config/packages/catalyst_auth.yaml | 8 - config/packages/catalyst_menu.yaml | 4 - config/routes/item.yaml | 34 ---- src/Controller/ItemController.php | 188 ----------------------- src/Controller/ItemPricingController.php | 63 ++++++-- src/Entity/{Item.php => ItemPrice.php} | 4 +- src/Entity/ItemType.php | 2 +- src/Entity/PriceTier.php | 8 +- 8 files changed, 56 insertions(+), 255 deletions(-) delete mode 100644 config/routes/item.yaml delete mode 100644 src/Controller/ItemController.php rename src/Entity/{Item.php => ItemPrice.php} (98%) diff --git a/config/packages/catalyst_auth.yaml b/config/packages/catalyst_auth.yaml index 7e02d5c0..9707fc93 100644 --- a/config/packages/catalyst_auth.yaml +++ b/config/packages/catalyst_auth.yaml @@ -667,14 +667,6 @@ catalyst_auth: acls: - id: item.menu label: Menu - - id: item.list - label: List - - id: item.add - label: Add - - id: item.update - label: Update - - id: item.delete - label: Delete - id: item_pricing label: Item Pricing acls: diff --git a/config/packages/catalyst_menu.yaml b/config/packages/catalyst_menu.yaml index a6bd1583..7972a414 100644 --- a/config/packages/catalyst_menu.yaml +++ b/config/packages/catalyst_menu.yaml @@ -306,7 +306,3 @@ catalyst_menu: acl: item_pricing.update label: Item Pricing parent: item - - id: item_list - acl: item.list - label: Items - parent: item diff --git a/config/routes/item.yaml b/config/routes/item.yaml deleted file mode 100644 index 89dde4d2..00000000 --- a/config/routes/item.yaml +++ /dev/null @@ -1,34 +0,0 @@ -item_list: - path: /items - controller: App\Controller\ItemController::index - methods: [GET] - -item_rows: - path: /items/rowdata - controller: App\Controller\ItemController::datatableRows - methods: [POST] - -item_add_form: - path: /items/newform - controller: App\Controller\ItemController::addForm - methods: [GET] - -item_add_submit: - path: /items - controller: App\Controller\ItemController::addSubmit - methods: [POST] - -item_update_form: - path: /items/{id} - controller: App\Controller\ItemController::updateForm - methods: [GET] - -item_update_submit: - path: /items/{id} - controller: App\Controller\ItemController::updateSubmit - methods: [POST] - -item_delete: - path: /items/{id} - controller: App\Controller\ItemController::deleteSubmit - methods: [DELETE] diff --git a/src/Controller/ItemController.php b/src/Controller/ItemController.php deleted file mode 100644 index 7e9cd277..00000000 --- a/src/Controller/ItemController.php +++ /dev/null @@ -1,188 +0,0 @@ -render('item/list.html.twig'); - } - - /** - * @IsGranted("item.list") - */ - public function datatableRows(Request $req) - { - // get query builder - $total_qb = $this->getDoctrine() - ->getRepository(Item::class) - ->createQueryBuilder('q'); - - // get datatable params - $datatable = $req->request->get('datatable'); - - // count total records - $tquery = $total_qb->select('COUNT(q)') - ->leftJoin(Battery::class, 'battery', Join::WITH, 'battery.id = q.item_id') - ->leftJoin(ServiceOffering::class, 'so', Join::WITH, 'so.id = q.item_id'); - $this->setQueryFilters($datatable, $tquery); - $total = $tquery->getQuery() - ->getSingleScalarResult(); - - // get current page number - $page = $datatable['pagination']['page'] ?? 1; - - $perpage = $datatable['pagination']['perpage']; - $offset = ($page - 1) * $perpage; - - // add metadata - $meta = [ - 'page' => $page, - 'perpage' => $perpage, - 'pages' => ceil($total / $perpage), - 'total' => $total, - 'sort' => 'asc', - 'field' => 'id' - ]; - - // reset query builder - $qb = $this->getDoctrine() - ->getRepository(Item::class) - ->createQueryBuilder('q'); - - // build query - $query = $qb->select('q') - ->leftJoin(Battery::class, 'battery', Join::WITH, 'battery.id = q.item_id') - ->leftJoin(ServiceOffering::class, 'so', Join::WITH, 'so.id = q.item_id'); - $this->setQueryFilters($datatable, $query); - - // check if sorting is present, otherwise use default - if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) { - $order = $datatable['sort']['sort'] ?? 'asc'; - $query->orderBy('q.' . $datatable['sort']['field'], $order); - } else { - $query->orderBy('q.id', 'asc'); - } - - // get rows for this page - $obj_rows = $query->setFirstResult($offset) - ->setMaxResults($perpage) - ->getQuery() - ->getResult(); - - // process rows - $rows = []; - foreach ($obj_rows as $orow) { - // add row data - $row['id'] = $orow->getID(); - - // add row metadata - $row['meta'] = [ - 'update_url' => '', - 'delete_url' => '' - ]; - - // add crud urls - if ($this->isGranted('item_price.update')) - $row['meta']['update_url'] = $this->generateUrl('item_price_update_form', ['id' => $row['id']]); - if ($this->isGranted('item_price.delete')) - $row['meta']['delete_url'] = $this->generateUrl('item_price_delete', ['id' => $row['id']]); - - $rows[] = $row; - } - - // response - return $this->json([ - 'meta' => $meta, - 'data' => $rows - ]); - } - - /** - * @Menu(selected="item.list") - * @IsGranted("item.add") - */ - public function addForm(EntityManagerInterface $em) - { - $item = new Item(); - - // get the sets for the dropdowns - $sets = $this->generateFormSets($em); - - $params = [ - 'obj' => $item, - 'sets' => $sets, - 'mode' => 'create', - ]; - - // response - return $this->render('item/form.html.twig', $params); - } - - protected function generateFormSets(EntityManagerInterface $em) - { - // item types - $item_types = $em->getRepository(ItemType::class)->findby([], ['name' => 'asc']); - $item_type_set = []; - foreach ($item_types as $it) - { - $item_type_set[$it->getID()] = $it->getName(); - } - - // batteries - $batts = $em->getRepository(Battery::class)->findAll(); - $batt_set = []; - foreach ($batts as $batt) - { - $batt_set[$batt->getID()] = $batt->getModel()->getName() . ' ' . $batt->getSize()->getName(); - } - - // service offerings - $services = $em->getRepository(ServiceOffering::class)->findBy([],['name' => 'asc']); - $service_set = []; - foreach ($services as $service) - { - $service_set[$service->getID()] = $service->getName(); - } - - return [ - 'item_types' => $item_type_set, - 'batteries' => $batt_set, - 'services' => $service_set, - ]; - } - - protected function setQueryFilters($datatable, QueryBuilder $query) - { - // TODO: add filter for item type. - // TODO: fix filter for name since name is with the associated entity - if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) { - $query->where('q.name LIKE :filter') - ->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%'); - } - } -} diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php index f7fd78ec..ac093e82 100644 --- a/src/Controller/ItemPricingController.php +++ b/src/Controller/ItemPricingController.php @@ -63,25 +63,26 @@ class ItemPricingController extends Controller // get the item type $item_type = $em->getRepository(ItemType::class)->find($it_id); + if ($item_type->getCode() == 'battery') + { + // get batteries + $items = $em->getRepository(Battery::class)->findBy(['flag_active' => true], ['id' => 'asc']); + } + else + { + // get service offerings + $items = $em->getRepository(ServiceOffering::class)->findBy([], ['id' => 'asc']); + } + + // on default price tier if ($pt_id == 0) { // default price tier, update battery or service offering, depending on item type // NOTE: battery and service offering prices or fees are stored as decimal. - if ($item_type->getCode() == 'battery') - { - // get batteries - $items = $em->getRepository(Battery::class)->findBy(['flag_active' => true], ['id' => 'asc']); - } - else - { - // get service offerings - $items = $em->getRepository(ServiceOffering::class)->findBy([], ['id' => 'asc']); - } - foreach ($items as $item) { $item_id = $item->getID(); - if (isset[$prices[$item_id]]) + if (isset($prices[$item_id])) { // check item type if ($item_type->getCode() == 'battery') @@ -96,8 +97,42 @@ class ItemPricingController extends Controller // get the price tier $price_tier = $em->getRepository(PriceTier::class)->find($pt_id); - // TODO: finish this + $item_prices = $price_tier->getItems(); + + // clear the tier's item prices + foreach ($item_prices as $ip) + { + $em->remove($ip); + } + + // update the tier's item prices + foreach ($items as $item) + { + $item_id = $item->getID(); + + $item_price = new ItemPrice(); + + $item_price->setItemType($item_type) + ->setPriceTier($price_tier) + ->setItemID($item_id); + + if (isset($prices[$item_id])) + { + $item_price->setPrice($price[$item_id] * 100); + } + else + { + $item_price->setPrice($item->getPrice() * 100); + } + + // save + $em->persist($item_price); + } } + + $em->flush(); + + return $this->redirectToRoute('item_pricing'); } /** @@ -117,7 +152,7 @@ class ItemPricingController extends Controller $pt = $em->getRepository(PriceTier::class)->find($pt_id); // get the items under the price tier - $pt_items = $pt->getItems(); + $pt_items = $pt->getItemPrices(); foreach ($pt_items as $pt_item) { // make item price hash diff --git a/src/Entity/Item.php b/src/Entity/ItemPrice.php similarity index 98% rename from src/Entity/Item.php rename to src/Entity/ItemPrice.php index 2e5ed091..143833ee 100644 --- a/src/Entity/Item.php +++ b/src/Entity/ItemPrice.php @@ -9,7 +9,7 @@ use Doctrine\ORM\Mapping as ORM; * @ORM\Table(name="item") */ -class Item +class ItemPrice { // unique id /** @@ -20,7 +20,7 @@ class Item protected $id; /** - * @ORM\ManyToOne(targetEntity="PriceTier", inversedBy="items") + * @ORM\ManyToOne(targetEntity="PriceTier", inversedBy="item_prices") * @ORM\JoinColumn(name="price_tier_id", referencedColumnName="id") */ protected $price_tier; diff --git a/src/Entity/ItemType.php b/src/Entity/ItemType.php index 3ef422d8..8c24e65c 100644 --- a/src/Entity/ItemType.php +++ b/src/Entity/ItemType.php @@ -37,7 +37,7 @@ class ItemType // items under an item type /** - * @ORM\OneToMany(targetEntity="Item", mappedBy="item_type") + * @ORM\OneToMany(targetEntity="ItemPrice", mappedBy="item_type") */ protected $items; diff --git a/src/Entity/PriceTier.php b/src/Entity/PriceTier.php index d929fc8f..1e2599a5 100644 --- a/src/Entity/PriceTier.php +++ b/src/Entity/PriceTier.php @@ -34,9 +34,9 @@ class PriceTier // items under a price tier /** - * @ORM\OneToMany(targetEntity="Item", mappedBy="price_tier") + * @ORM\OneToMany(targetEntity="ItemPrice", mappedBy="price_tier") */ - protected $items; + protected $item_prices; public function __construct() { @@ -80,9 +80,9 @@ class PriceTier return $this; } - public function getItems() + public function getItemPrices() { - return $this->items; + return $this->item_prices; } } From 9de6fa7999c7465ffad6ba0bb649a1f7529a822b Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Wed, 17 Jan 2024 14:25:28 +0800 Subject: [PATCH 15/29] Fix issues found during saving of item prices. #780 --- src/Controller/ItemPricingController.php | 16 +++++++++++----- src/Entity/ItemPrice.php | 2 +- templates/item-pricing/form.html.twig | 3 ++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php index ac093e82..f5e2a532 100644 --- a/src/Controller/ItemPricingController.php +++ b/src/Controller/ItemPricingController.php @@ -20,6 +20,7 @@ use App\Entity\PriceTier; use App\Entity\Battery; use App\Entity\ServiceOffering; use App\Entity\ItemType; +use App\Entity\ItemPrice; class ItemPricingController extends Controller { @@ -39,12 +40,16 @@ class ItemPricingController extends Controller // load only batteries upon initial loading $items = $this->getBatteries($em); + // set the default item type to battery + $default_it = $em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']); + $params = [ 'sets' => [ 'price_tiers' => $price_tiers, 'item_types' => $item_types, ], 'items' => $items, + 'default_item_type_id' => $default_it->getID(), ]; return $this->render('item-pricing/form.html.twig', $params); @@ -97,7 +102,7 @@ class ItemPricingController extends Controller // get the price tier $price_tier = $em->getRepository(PriceTier::class)->find($pt_id); - $item_prices = $price_tier->getItems(); + $item_prices = $price_tier->getItemPrices(); // clear the tier's item prices foreach ($item_prices as $ip) @@ -118,7 +123,7 @@ class ItemPricingController extends Controller if (isset($prices[$item_id])) { - $item_price->setPrice($price[$item_id] * 100); + $item_price->setPrice($prices[$item_id] * 100); } else { @@ -153,10 +158,11 @@ class ItemPricingController extends Controller // get the items under the price tier $pt_items = $pt->getItemPrices(); + foreach ($pt_items as $pt_item) { // make item price hash - $pt_prices[$pt_item->getID()] = $pt_item->getPrice(); + $pt_prices[$pt_item->getItemID()] = $pt_item->getPrice(); } } @@ -192,10 +198,10 @@ class ItemPricingController extends Controller // check if tier has price for item if (isset($pt_prices[$item_id])) { - $price = $pt_prices[$item_id]; + $pt_price = $pt_prices[$item_id]; // actual price - $actual_price = number_format($price / 100, 2, '.', ''); + $price = number_format($pt_price / 100, 2, '.', ''); } $actual_price = $price; diff --git a/src/Entity/ItemPrice.php b/src/Entity/ItemPrice.php index 143833ee..48130468 100644 --- a/src/Entity/ItemPrice.php +++ b/src/Entity/ItemPrice.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity - * @ORM\Table(name="item") + * @ORM\Table(name="item_price") */ class ItemPrice diff --git a/templates/item-pricing/form.html.twig b/templates/item-pricing/form.html.twig index 0afd23e3..4572a46a 100644 --- a/templates/item-pricing/form.html.twig +++ b/templates/item-pricing/form.html.twig @@ -51,7 +51,7 @@ - +
@@ -118,6 +118,7 @@ function init_item_type_dropdown() { } function load_prices(price_tier_id, item_type_id) { + console.log('loading prices'); var req = new XMLHttpRequest(); req.onreadystatechange = function() { // process response From ee033ddd552719e99b8a90acd1770d964b193600 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Wed, 17 Jan 2024 15:31:20 +0800 Subject: [PATCH 16/29] Fix issues found during testing. #780 --- src/Controller/ItemPricingController.php | 5 +++-- templates/item-pricing/form.html.twig | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Controller/ItemPricingController.php b/src/Controller/ItemPricingController.php index f5e2a532..3129f362 100644 --- a/src/Controller/ItemPricingController.php +++ b/src/Controller/ItemPricingController.php @@ -104,10 +104,11 @@ class ItemPricingController extends Controller $item_prices = $price_tier->getItemPrices(); - // clear the tier's item prices + // clear the tier's item prices for the specific item type foreach ($item_prices as $ip) { - $em->remove($ip); + if ($ip->getItemType() == $item_type) + $em->remove($ip); } // update the tier's item prices diff --git a/templates/item-pricing/form.html.twig b/templates/item-pricing/form.html.twig index 4572a46a..d6099390 100644 --- a/templates/item-pricing/form.html.twig +++ b/templates/item-pricing/form.html.twig @@ -118,7 +118,6 @@ function init_item_type_dropdown() { } function load_prices(price_tier_id, item_type_id) { - console.log('loading prices'); var req = new XMLHttpRequest(); req.onreadystatechange = function() { // process response From 70ee7fdd8972da0e400f3b36da6d1908478eee7b Mon Sep 17 00:00:00 2001 From: root Date: Fri, 19 Jan 2024 04:14:06 -0500 Subject: [PATCH 17/29] Add invoice rule for price tier. #782 --- src/InvoiceRule/PriceTier.php | 157 +++++++++++++++++++++++++++++++++ src/Ramcar/InvoiceCriteria.php | 12 +++ 2 files changed, 169 insertions(+) create mode 100644 src/InvoiceRule/PriceTier.php diff --git a/src/InvoiceRule/PriceTier.php b/src/InvoiceRule/PriceTier.php new file mode 100644 index 00000000..4db12b55 --- /dev/null +++ b/src/InvoiceRule/PriceTier.php @@ -0,0 +1,157 @@ +em = $em; + } + + public function getID() + { + return 'price_tier'; + } + + public function compute($criteria, &$total) + { + $pt_id = $criteria->getPriceTier(); + + // get the service type + $service_type = $criteria->getServiceType(); + + // get price tier + $pt = $em->getRepository(PTEntity::class)->find($pt_id); + + // price tier is default + if ($pt == null) + { + // check if service type is battery sales + if ($service_type == ServiceType::BATTERY_REPLACEMENT_NEW) + { + $items = $this->processBatteryEntries($criteria, $total); + } + } + else + { + // get items in price tier + $pt_items = $pt->getItemPrices(); + + foreach ($pt_items as $pt_item) + { + // make item price hash + $pt_prices[$pt_item->getItemID()] = $pt_item->getPrice(); + } + } + + return $items; + } + + public function validatePromo($criteria, $promo_id) + { + return false; + } + + 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) && + ($stype != ServiceType::BATTERY_REPLACEMENT_WARRANTY)) + 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) + { + $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; + + // 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; + } + + protected function processBatteryEntries($criteria, &$total) + { + $items = []; + + // get the entries + $entries = $criteria->getEntries(); + foreach($entries as $entry) + { + $batt = $entry['battery']; + $qty = $entry['qty']; + $trade_in = null; + + if (isset($entry['trade_in'])) + $trade_in = $entry['trade_in']; + + $size = $batt->getSize(); + + if ($trade_in == null) + { + // battery purchase + $price = $batt->getSellingPrice(); + + $items[] = [ + 'service_type' => $this->getID(), + 'battery' => $batt, + 'qty' => $qty, + 'title' => $this->getTitle($criteria->getServiceType(), $batt), + 'price' => $price, + ]; + + $qty_price = bcmul($price, $qty, 2); + + $total['sell_price'] = bcadd($total['sell_price'], $qty_price, 2); + $total['total_price'] = bcadd($total['total_price'], $qty_price, 2); + } + } + + return $items; + } + + protected function getTitle($service_type, $battery) + { + $title =''; + + // TODO: check for service type + $title = $battery->getModel()->getName() . ' ' . $battery->getSize()->getName(); + + return $title; + } + +} diff --git a/src/Ramcar/InvoiceCriteria.php b/src/Ramcar/InvoiceCriteria.php index b27395da..18ebd82a 100644 --- a/src/Ramcar/InvoiceCriteria.php +++ b/src/Ramcar/InvoiceCriteria.php @@ -17,6 +17,7 @@ class InvoiceCriteria protected $service_charges; protected $flag_taxable; protected $source; // use Ramcar's TransactionOrigin + protected $price_tier; // entries are battery and trade-in combos protected $entries; @@ -32,6 +33,7 @@ class InvoiceCriteria $this->service_charges = []; $this->flag_taxable = false; $this->source = ''; + $this->price_tier = 0; // set to default } public function setServiceType($stype) @@ -179,4 +181,14 @@ class InvoiceCriteria return $this->source; } + public function setPriceTier($price_tier) + { + $this->price_tier = $price_tier; + return $this; + } + + public function getPriceTier() + { + return $this->price_tier; + } } From 29ad8d57a44ad1990cfb0e33990b52895f305892 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Mon, 22 Jan 2024 04:24:41 -0500 Subject: [PATCH 18/29] Add processing of battery entries and invoice item titles. #782 --- src/InvoiceRule/PriceTier.php | 77 +++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/src/InvoiceRule/PriceTier.php b/src/InvoiceRule/PriceTier.php index 4db12b55..7db80ccc 100644 --- a/src/InvoiceRule/PriceTier.php +++ b/src/InvoiceRule/PriceTier.php @@ -38,11 +38,18 @@ class PriceTier implements InvoiceRuleInterface // price tier is default if ($pt == null) { - // check if service type is battery sales - if ($service_type == ServiceType::BATTERY_REPLACEMENT_NEW) + // check if service type is battery sales and battery warranty (sometimes they add a battery + // for battery warranty + if (($service_type == ServiceType::BATTERY_REPLACEMENT_NEW) || + ($service_type == ServiceType::BATTERY_REPLACEMENT_WARRANTY)) { $items = $this->processBatteryEntries($criteria, $total); } + else + { + // TODO: process the service fees + $items = $this->processServiceEntries($criteria, $total); + } } else { @@ -54,6 +61,8 @@ class PriceTier implements InvoiceRuleInterface // make item price hash $pt_prices[$pt_item->getItemID()] = $pt_item->getPrice(); } + + // TODO: finish this } return $items; @@ -123,14 +132,17 @@ class PriceTier implements InvoiceRuleInterface if ($trade_in == null) { - // battery purchase - $price = $batt->getSellingPrice(); + // check if battery purchase or battery replacement + if ($service_type == ServiceType::BATTERY_REPLACEMENT_NEW) + $price = $batt->getSellingPrice(); + else + $price = 0; $items[] = [ 'service_type' => $this->getID(), 'battery' => $batt, 'qty' => $qty, - 'title' => $this->getTitle($criteria->getServiceType(), $batt), + 'title' => $this->getItemTitle($criteria->getServiceType(), $batt), 'price' => $price, ]; @@ -144,12 +156,63 @@ class PriceTier implements InvoiceRuleInterface return $items; } - protected function getTitle($service_type, $battery) + protected function processServiceEntries($criteria, &$total) + { + } + + protected function getItemTitle($service_type, $battery) { $title =''; // TODO: check for service type - $title = $battery->getModel()->getName() . ' ' . $battery->getSize()->getName(); + switch ($service_type) { + case (ServiceType::BATTERY_REPLACEMENT_NEW): + $title = $battery->getModel()->getName() . ' ' . $battery->getSize()->getName(); + break; + case (ServiceType::BATTERY_REPLACEMENT_WARRANTY): + $title = $battery->getModel()->getName() . ' ' . $battery->getSize()->getName() . ' - Service Unit'; + break; + default: + $title = ''; + break; + } + + return $title; + + protected function getServiceTitle($service_type, $fuel_type) + { + $title = ''; + + switch ($service_type) { + case (ServiceType::JUMPSTART_TROUBLESHOOT): + case (ServiceType::JUMPSTART_WARRANTY): + $title = 'Service - Troubleshooting fee'; + break; + case (ServiceType::OVERHEAT_ASSISTANCE): + $title = 'Service - ' . ServiceType::getName(ServiceType::OVERHEAT_ASSISTANCE); + break; + case (ServiceType::POST_RECHARGED): + $title = 'Recharge fee'; + break; + case (ServiceType::POST_REPLACEMENT): + $title = 'Battery replacement'; + break; + case (ServiceType::TIRE_REPAIR): + $title = 'Service - Flat Tire'; + break; + case (ServiceType::EMERGENCY_REFUEL): + $title = '4L - ' . ucfirst($fuel_type); + break; + default: + $title = ''; + } + + return $title; + } + + protected function getServiceCoolantTitle() + { + $title = '4L Coolant'; return $title; } From b6763bfd3e41452a48048407120fa187f7058fcd Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Wed, 24 Jan 2024 02:24:13 -0500 Subject: [PATCH 19/29] Add price tier checking for battery sales. #782 --- config/services.yaml | 5 ++ src/Controller/JobOrderController.php | 22 ++++++- src/InvoiceRule/BatterySales.php | 38 +++++++++++- .../InvoiceGenerator/CMBInvoiceGenerator.php | 2 +- .../InvoiceGenerator/ResqInvoiceGenerator.php | 2 +- src/Service/InvoiceGeneratorInterface.php | 3 +- src/Service/InvoiceManager.php | 12 ++-- .../JobOrderHandler/ResqJobOrderHandler.php | 9 ++- src/Service/PriceTierManager.php | 58 +++++++++++++++++++ templates/job-order/form.html.twig | 6 +- 10 files changed, 141 insertions(+), 16 deletions(-) create mode 100644 src/Service/PriceTierManager.php diff --git a/config/services.yaml b/config/services.yaml index b19ecabc..d20db360 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -310,3 +310,8 @@ services: arguments: $server_key: "%env(FCM_SERVER_KEY)%" $sender_id: "%env(FCM_SENDER_ID)%" + + # price tier manager + App\Service\PriceTierManager: + arguments: + $em: "@doctrine.orm.entity_manager" diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index 91921a0f..17c62a45 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -30,6 +30,7 @@ use App\Service\HubSelector; use App\Service\RiderTracker; use App\Service\MotivConnector; +use App\Service\PriceTierManager; use App\Service\GeofenceTracker; use Symfony\Component\HttpFoundation\Request; @@ -42,6 +43,8 @@ use Doctrine\ORM\EntityManagerInterface; use Catalyst\MenuBundle\Annotation\Menu; +use CrEOF\Spatial\PHP\Types\Geometry\Point; + class JobOrderController extends Controller { public function getJobOrders(Request $req, JobOrderHandlerInterface $jo_handler) @@ -741,7 +744,7 @@ class JobOrderController extends Controller } - public function generateInvoice(Request $req, InvoiceGeneratorInterface $ic) + public function generateInvoice(Request $req, InvoiceGeneratorInterface $ic, PriceTierManager $pt_manager) { // error_log('generating invoice...'); $error = false; @@ -752,6 +755,19 @@ class JobOrderController extends Controller $cvid = $req->request->get('cvid'); $service_charges = $req->request->get('service_charges', []); + // coordinates + // need to check if lng and lat are set + $lng = $req->request->get('coord_lng', 0); + $lat = $req->request->get('coord_lat', 0); + + $price_tier = 0; + if (($lng != 0) && ($lat != 0)) + { + $coordinates = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); + $price_tier = $pt_manager->getPriceTier($coordinates); + } + + $em = $this->getDoctrine()->getManager(); // get customer vehicle @@ -767,8 +783,8 @@ class JobOrderController extends Controller $criteria->setServiceType($stype) ->setCustomerVehicle($cv) ->setIsTaxable() - ->setSource(TransactionOrigin::CALL); - + ->setSource(TransactionOrigin::CALL) + ->setPriceTier($price_tier); /* // if it's a jumpstart or troubleshoot only, we know what to charge already diff --git a/src/InvoiceRule/BatterySales.php b/src/InvoiceRule/BatterySales.php index 45b060d1..3ff9a995 100644 --- a/src/InvoiceRule/BatterySales.php +++ b/src/InvoiceRule/BatterySales.php @@ -10,14 +10,19 @@ use App\Ramcar\TradeInType; use App\Ramcar\ServiceType; use App\Entity\Battery; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; class BatterySales implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -28,6 +33,9 @@ class BatterySales implements InvoiceRuleInterface public function compute($criteria, &$total) { $stype = $criteria->getServiceType(); + $pt = $criteria->getPriceTier(); + + error_log('price tier ' . $pt); $items = []; if ($stype == $this->getID()) @@ -47,8 +55,13 @@ class BatterySales implements InvoiceRuleInterface if ($trade_in == null) { - // battery purchase - $price = $batt->getSellingPrice(); + // check if price tier has item price for battery + $pt_price = $this->getPriceTierItemPrice($pt, $batt); + + if ($pt_price == null) + $price = $batt->getSellingPrice(); + else + $price = $pt_price; $items[] = [ 'service_type' => $this->getID(), @@ -114,6 +127,25 @@ class BatterySales implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id, $batt) + { + // price tier is default + if ($pt_id == 0) + return null; + + // find the item type battery + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']); + if ($item_type == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $batt->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + protected function getTitle($battery) { $title = $battery->getModel()->getName() . ' ' . $battery->getSize()->getName(); diff --git a/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php b/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php index 5dc86f8d..c1f40509 100644 --- a/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php +++ b/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php @@ -134,7 +134,7 @@ class CMBInvoiceGenerator implements InvoiceGeneratorInterface } // generate invoice criteria - public function generateInvoiceCriteria($jo, $discount, $invoice_items, $source = null, &$error_array) + public function generateInvoiceCriteria($jo, $discount, $invoice_items, $price_tier = null, $source = null, &$error_array) { $em = $this->em; diff --git a/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php b/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php index 3809133b..efe0a9de 100644 --- a/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php +++ b/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php @@ -144,7 +144,7 @@ class ResqInvoiceGenerator implements InvoiceGeneratorInterface } // generate invoice criteria - public function generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source = null, &$error_array) + public function generateInvoiceCriteria($jo, $promo_id, $invoice_items, $price_tier = null, $source = null, &$error_array) { $em = $this->em; diff --git a/src/Service/InvoiceGeneratorInterface.php b/src/Service/InvoiceGeneratorInterface.php index e2cb2cc3..4c46c38f 100644 --- a/src/Service/InvoiceGeneratorInterface.php +++ b/src/Service/InvoiceGeneratorInterface.php @@ -4,6 +4,7 @@ namespace App\Service; use App\Entity\Invoice; use App\Entity\JobOrder; +use App\Entity\PriceTier; use App\Ramcar\InvoiceCriteria; @@ -13,7 +14,7 @@ interface InvoiceGeneratorInterface public function generateInvoice(InvoiceCriteria $criteria); // generate invoice criteria - public function generateInvoiceCriteria(JobOrder $jo, int $promo_id, array $invoice_items, $source, array &$error_array); + public function generateInvoiceCriteria(JobOrder $jo, int $promo_id, array $invoice_items, $source, PriceTier $price_tier, array &$error_array); // prepare draft for invoice public function generateDraftInvoice(InvoiceCriteria $criteria, int $promo_id, array $service_charges, array $items); diff --git a/src/Service/InvoiceManager.php b/src/Service/InvoiceManager.php index 65fc3a43..93d6bafd 100644 --- a/src/Service/InvoiceManager.php +++ b/src/Service/InvoiceManager.php @@ -10,6 +10,7 @@ use Doctrine\ORM\EntityManagerInterface; use App\InvoiceRule; use App\Service\InvoiceGeneratorInterface; +use App\Service\PriceTierManager; use App\Ramcar\InvoiceCriteria; use App\Ramcar\InvoiceStatus; @@ -28,12 +29,14 @@ class InvoiceManager implements InvoiceGeneratorInterface protected $em; protected $validator; protected $available_rules; + protected $pt_manager; - public function __construct(EntityManagerInterface $em, Security $security, ValidatorInterface $validator) + public function __construct(EntityManagerInterface $em, Security $security, ValidatorInterface $validator, PriceTierManager $pt_manager) { $this->em = $em; $this->security = $security; $this->validator = $validator; + $this->pt_manager = $pt_manager; $this->available_rules = $this->getAvailableRules(); } @@ -42,7 +45,7 @@ class InvoiceManager implements InvoiceGeneratorInterface { // TODO: get list of invoice rules from .env or a json file? return [ - new InvoiceRule\BatterySales($this->em), + new InvoiceRule\BatterySales($this->em, $this->pt_manager), new InvoiceRule\BatteryReplacementWarranty($this->em), new InvoiceRule\Jumpstart($this->em), new InvoiceRule\JumpstartWarranty($this->em), @@ -58,12 +61,13 @@ class InvoiceManager implements InvoiceGeneratorInterface } // this is called when JO is submitted - public function generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source, &$error_array) + public function generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source, $price_tier, &$error_array) { // instantiate the invoice criteria $criteria = new InvoiceCriteria(); $criteria->setServiceType($jo->getServiceType()) - ->setCustomerVehicle($jo->getCustomerVehicle()); + ->setCustomerVehicle($jo->getCustomerVehicle()) + ->setPriceTier($price_tier); // set if taxable // NOTE: ideally, this should be a parameter when calling generateInvoiceCriteria. But that diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 361c0b22..28e9035d 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -69,6 +69,7 @@ use App\Service\HubSelector; use App\Service\HubDistributor; use App\Service\HubFilteringGeoChecker; use App\Service\JobOrderManager; +use App\Service\PriceTierManager; use CrEOF\Spatial\PHP\Types\Geometry\Point; @@ -96,6 +97,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface protected $cust_distance_limit; protected $hub_filter_enable; protected $jo_manager; + protected $pt_manager; protected $template_hash; @@ -104,7 +106,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface TranslatorInterface $translator, RiderAssignmentHandlerInterface $rah, string $country_code, WarrantyHandler $wh, RisingTideGateway $rt, PromoLogger $promo_logger, HubDistributor $hub_dist, HubFilteringGeoChecker $hub_geofence, - string $cust_distance_limit, string $hub_filter_enabled, JobOrderManager $jo_manager) + string $cust_distance_limit, string $hub_filter_enabled, JobOrderManager $jo_manager, PriceTierManager $pt_manager) { $this->em = $em; $this->ic = $ic; @@ -121,6 +123,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $this->cust_distance_limit = $cust_distance_limit; $this->hub_filter_enabled = $hub_filter_enabled; $this->jo_manager = $jo_manager; + $this->pt_manager = $pt_manager; $this->loadTemplates(); } @@ -585,7 +588,9 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface { $source = $jo->getSource(); - $this->ic->generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source, $error_array); + // TODO: set the price tier according to location. + $price_tier = $this->pt_manager->getPriceTier($jo->getCoordinates()); + $this->ic->generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source, $price_tier, $error_array); } // validate diff --git a/src/Service/PriceTierManager.php b/src/Service/PriceTierManager.php new file mode 100644 index 00000000..e4174e78 --- /dev/null +++ b/src/Service/PriceTierManager.php @@ -0,0 +1,58 @@ +em = $em; + } + + public function getItemPrice($pt_id, $item_type_id, $item_id) + { + // find the item price, given the price tier, battery id, and item type (battery) + $db_conn = $this->em->getConnection(); + + $ip_sql = 'SELECT ip.price AS price + FROM item_price ip + WHERE ip.price_tier_id = :pt_id + AND ip.item_type_id = :it_id + AND ip.item_id = :item_id'; + + $ip_stmt = $db_conn->prepare($ip_sql); + $ip_stmt->bindValue('pt_id', $pt_id); + $ip_stmt->bindValue('it_id', $item_type_id); + $ip_stmt->bindValue('item_id', $item_id); + + $ip_result = $ip_stmt->executeQuery(); + + $actual_price = 0; + // go through rows + while ($row = $ip_result->fetchAssociative()) + { + // get the price + $price = $row['price']; + + // actual price + $actual_price = number_format($price / 100, 2, '.', ''); + } + + return $actual_price; + } + + public function getPriceTier(Point $point) + { + // TODO: get location's price tier, given a set of coordinates + // for now, hardcoded for testing purposes + return 1; + } +} diff --git a/templates/job-order/form.html.twig b/templates/job-order/form.html.twig index ca68e922..375e0353 100644 --- a/templates/job-order/form.html.twig +++ b/templates/job-order/form.html.twig @@ -1761,6 +1761,8 @@ $(function() { var table = $("#invoice-table tbody"); var stype = $("#service_type").val(); var cvid = $("#customer-vehicle").val(); + var lng = $("#map_lng").val(); + var lat = $("#map_lat").val(); console.log(JSON.stringify(invoiceItems)); @@ -1772,7 +1774,9 @@ $(function() { 'stype': stype, 'items': invoiceItems, 'promo': promo, - 'cvid': cvid + 'cvid': cvid, + 'coord_lng': lng, + 'coord_lat': lat, } }).done(function(response) { // mark as invoice changed From c5b395d720589b6296c58bc158a80e211d8a3e7d Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Wed, 24 Jan 2024 04:02:14 -0500 Subject: [PATCH 20/29] Add price tier for battery replacement warranty. #782 --- .../BatteryReplacementWarranty.php | 41 ++++++++++++++++++- src/InvoiceRule/BatterySales.php | 2 - src/Service/InvoiceManager.php | 2 +- src/Service/PriceTierManager.php | 4 +- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/InvoiceRule/BatteryReplacementWarranty.php b/src/InvoiceRule/BatteryReplacementWarranty.php index ee1497b5..8c2dafd4 100644 --- a/src/InvoiceRule/BatteryReplacementWarranty.php +++ b/src/InvoiceRule/BatteryReplacementWarranty.php @@ -11,14 +11,19 @@ use App\Ramcar\TradeInType; use App\Entity\Battery; use App\Entity\ServiceOffering; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; class BatteryReplacementWarranty implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -29,6 +34,7 @@ class BatteryReplacementWarranty implements InvoiceRuleInterface public function compute($criteria, &$total) { $stype = $criteria->getServiceType(); + $pt_id = $criteria->getPriceTier(); $items = []; if ($stype == $this->getID()) @@ -40,7 +46,14 @@ class BatteryReplacementWarranty implements InvoiceRuleInterface { $batt = $entry['battery']; $qty = 1; - $price = $this->getServiceTypeFee(); + + // check if price tier has item price + $pt_price = $this->getPriceTierItemPrice($pt_id); + + if ($pt_price == null) + $price = $this->getServiceTypeFee(); + else + $price = $pt_price; $items[] = [ 'service_type' => $this->getID(), @@ -117,6 +130,30 @@ class BatteryReplacementWarranty implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id) + { + // price_tier is default + if ($pt_id == 0) + return null; + + // find the item type for service offering + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + if ($item_type == null) + return null; + + // find the service offering + $code = 'battery_replacement_warranty_fee'; + $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + + $item_type_id = $item_type->getID(); + $item_id = $service->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + + protected function getTitle($battery) { $title = $battery->getModel()->getName() . ' ' . $battery->getSize()->getName() . ' - Service Unit'; diff --git a/src/InvoiceRule/BatterySales.php b/src/InvoiceRule/BatterySales.php index 3ff9a995..645e545b 100644 --- a/src/InvoiceRule/BatterySales.php +++ b/src/InvoiceRule/BatterySales.php @@ -35,8 +35,6 @@ class BatterySales implements InvoiceRuleInterface $stype = $criteria->getServiceType(); $pt = $criteria->getPriceTier(); - error_log('price tier ' . $pt); - $items = []; if ($stype == $this->getID()) { diff --git a/src/Service/InvoiceManager.php b/src/Service/InvoiceManager.php index 93d6bafd..6e37a207 100644 --- a/src/Service/InvoiceManager.php +++ b/src/Service/InvoiceManager.php @@ -46,7 +46,7 @@ class InvoiceManager implements InvoiceGeneratorInterface // TODO: get list of invoice rules from .env or a json file? return [ new InvoiceRule\BatterySales($this->em, $this->pt_manager), - new InvoiceRule\BatteryReplacementWarranty($this->em), + new InvoiceRule\BatteryReplacementWarranty($this->em, $this->pt_manager), new InvoiceRule\Jumpstart($this->em), new InvoiceRule\JumpstartWarranty($this->em), new InvoiceRule\PostRecharged($this->em), diff --git a/src/Service/PriceTierManager.php b/src/Service/PriceTierManager.php index e4174e78..c572af76 100644 --- a/src/Service/PriceTierManager.php +++ b/src/Service/PriceTierManager.php @@ -35,7 +35,9 @@ class PriceTierManager $ip_result = $ip_stmt->executeQuery(); - $actual_price = 0; + // results found + $actual_price = null; + // go through rows while ($row = $ip_result->fetchAssociative()) { From bc6364ace52058a4e343c784c07016a55731bcea Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Thu, 25 Jan 2024 02:11:05 -0500 Subject: [PATCH 21/29] Add price tier to invoice rules. #782 --- .../BatteryReplacementWarranty.php | 4 + src/InvoiceRule/Fuel.php | 94 +++++++- src/InvoiceRule/Jumpstart.php | 60 ++++- src/InvoiceRule/JumpstartWarranty.php | 47 +++- src/InvoiceRule/Overheat.php | 55 ++++- src/InvoiceRule/PostRecharged.php | 47 +++- src/InvoiceRule/PostReplacement.php | 47 +++- src/InvoiceRule/PriceTier.php | 220 ------------------ src/InvoiceRule/TireRepair.php | 54 ++++- src/Service/InvoiceManager.php | 14 +- 10 files changed, 386 insertions(+), 256 deletions(-) delete mode 100644 src/InvoiceRule/PriceTier.php diff --git a/src/InvoiceRule/BatteryReplacementWarranty.php b/src/InvoiceRule/BatteryReplacementWarranty.php index 8c2dafd4..46ba5e8a 100644 --- a/src/InvoiceRule/BatteryReplacementWarranty.php +++ b/src/InvoiceRule/BatteryReplacementWarranty.php @@ -145,6 +145,10 @@ class BatteryReplacementWarranty implements InvoiceRuleInterface $code = 'battery_replacement_warranty_fee'; $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + // check if service is null. If null, return null + if ($service == null) + return null; + $item_type_id = $item_type->getID(); $item_id = $service->getID(); diff --git a/src/InvoiceRule/Fuel.php b/src/InvoiceRule/Fuel.php index f843e1c0..691a1ba2 100644 --- a/src/InvoiceRule/Fuel.php +++ b/src/InvoiceRule/Fuel.php @@ -11,14 +11,19 @@ use App\Ramcar\ServiceType; use App\Entity\ServiceOffering; use App\Entity\CustomerVehicle; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; class Fuel implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -29,6 +34,7 @@ class Fuel implements InvoiceRuleInterface public function compute($criteria, &$total) { $stype = $criteria->getServiceType(); + $pt_id = $criteria->getPriceTier(); $items = []; @@ -36,7 +42,13 @@ class Fuel implements InvoiceRuleInterface { $cv = $criteria->getCustomerVehicle(); - $fee = $this->getServiceTypeFee($cv); + // check if price tier has item price + $pt_price = $this->getPriceTierItemPrice($pt_id, $cv); + + if ($pt_price == null) + $service_price = $this->getServiceTypeFee($cv); + else + $service_price = $pt_price; $ftype = $cv->getFuelType(); @@ -46,10 +58,10 @@ class Fuel implements InvoiceRuleInterface 'service_type' => $this->getID(), 'qty' => $qty, 'title' => $this->getServiceTitle($ftype), - 'price' => $fee, + 'price' => $service_price, ]; - $qty_fee = bcmul($qty, $fee, 2); + $qty_fee = bcmul($qty, $service_price, 2); $total_price = $qty_fee; switch ($ftype) @@ -57,7 +69,15 @@ class Fuel implements InvoiceRuleInterface case FuelType::GAS: case FuelType::DIESEL: $qty = 1; - $price = $this->getFuelFee($ftype); + + // check if price tier has item price for fuel type + $pt_price = $this->getPriceTierFuelItemPrice($pt_id, $ftype); + + if ($pt_price == null) + $price = $this->getFuelFee($ftype); + else + $price = $pt_price; + $items[] = [ 'service_type' => $this->getID(), 'qty' => $qty, @@ -138,6 +158,70 @@ class Fuel implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id) + { + // price_tier is default + if ($pt_id == 0) + return null; + + // find the item type for service offering + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + if ($item_type == null) + return null; + + // find the service offering + // check if customer vehicle has a motolite battery + // if yes, set the code to the motolite user service fee + if ($cv->hasMotoliteBattery()) + $code = 'motolite_user_service_fee'; + else + $code = 'fuel_service_fee'; + + $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + + // check if service is null. If null, return null + if ($service == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $service->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + + protected function getPriceTierFuelItemPrice($pt_id, $fuel_type) + { + // price_tier is default + if ($pt_id == 0) + return null; + + // find the item type for service offering + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + if ($item_type == null) + return null; + + // find the service offering + $code = ''; + if ($fuel_type == FuelType::GAS) + $code = 'fuel_gas_fee'; + if ($fuel_type == FuelType::DIESEL) + $code = 'fuel_diesel_fee'; + $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + + // check if service is null. If null, return null + if ($service == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $service->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + protected function getTitle($fuel_type) { $title = '4L - ' . ucfirst($fuel_type); diff --git a/src/InvoiceRule/Jumpstart.php b/src/InvoiceRule/Jumpstart.php index dce41d99..d2e89b0a 100644 --- a/src/InvoiceRule/Jumpstart.php +++ b/src/InvoiceRule/Jumpstart.php @@ -8,16 +8,21 @@ use App\InvoiceRuleInterface; use App\Entity\ServiceOffering; use App\Entity\CustomerVehicle; +use App\Entity\ItemType; use App\Ramcar\TransactionOrigin; +use App\Service\PriceTierManager; + class Jumpstart implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -29,13 +34,21 @@ class Jumpstart implements InvoiceRuleInterface { $stype = $criteria->getServiceType(); $source = $criteria->getSource(); + $pt_id = $criteria->getPriceTier(); $items = []; if ($stype == $this->getID()) { $cv = $criteria->getCustomerVehicle(); - $fee = $this->getServiceTypeFee($source, $cv); + + // check if price tier has item price + $pt_price = $this->getPriceTierItemPrice($pt_id, $source, $cv); + + if ($pt_price == null) + $price = $this->getServiceTypeFee($source, $cv); + else + $price = $pt_price; // add the service fee to items $qty = 1; @@ -43,10 +56,10 @@ class Jumpstart implements InvoiceRuleInterface 'service_type' => $this->getID(), 'qty' => $qty, 'title' => $this->getServiceTitle(), - 'price' => $fee, + 'price' => $price, ]; - $qty_price = bcmul($fee, $qty, 2); + $qty_price = bcmul($price, $qty, 2); $total['total_price'] = bcadd($total['total_price'], $qty_price, 2); } @@ -86,6 +99,45 @@ class Jumpstart implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id, $source, $cv) + { + // price_tier is default + if ($pt_id == 0) + return null; + + // find the item type for service offering + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + if ($item_type == null) + return null; + + // find the service offering + // check the source of JO + // (1) if from app, service fee is 0 if motolite user. jumpstart fee for app if non-motolite user. + // (2) any other source, jumpstart fees are charged whether motolite user or not + if ($source == TransactionOrigin::MOBILE_APP) + { + if ($cv->hasMotoliteBattery()) + $code = 'motolite_user_service_fee'; + else + $code = 'jumpstart_fee_mobile_app'; + } + else + $code = 'jumpstart_fee'; + + $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + + // check if service is null. If null, return null + if ($service == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $service->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + protected function getServiceTitle() { $title = 'Service - Troubleshooting fee'; diff --git a/src/InvoiceRule/JumpstartWarranty.php b/src/InvoiceRule/JumpstartWarranty.php index 9423da44..4c6ac387 100644 --- a/src/InvoiceRule/JumpstartWarranty.php +++ b/src/InvoiceRule/JumpstartWarranty.php @@ -7,14 +7,19 @@ use Doctrine\ORM\EntityManagerInterface; use App\InvoiceRuleInterface; use App\Entity\ServiceOffering; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; class JumpstartWarranty implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -25,12 +30,19 @@ class JumpstartWarranty implements InvoiceRuleInterface public function compute($criteria, &$total) { $stype = $criteria->getServiceType(); + $pt_id = $criteria->getPriceTier(); $items = []; if ($stype == $this->getID()) { - $fee = $this->getServiceTypeFee(); + // check if price tier has item price + $pt_price = $this->getPriceTierItemPrice($pt_id); + + if ($pt_price == null) + $price = $this->getServiceTypeFee(); + else + $price = $pt_price; // add the service fee to items $qty = 1; @@ -38,10 +50,10 @@ class JumpstartWarranty implements InvoiceRuleInterface 'service_type' => $this->getID(), 'qty' => $qty, 'title' => $this->getServiceTitle(), - 'price' => $fee, + 'price' => $price, ]; - $qty_price = bcmul($fee, $qty, 2); + $qty_price = bcmul($price, $qty, 2); $total['total_price'] = bcadd($total['total_price'], $qty_price, 2); } @@ -72,6 +84,33 @@ class JumpstartWarranty implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id) + { + // price_tier is default + if ($pt_id == 0) + return null; + + // find the item type for service offering + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + if ($item_type == null) + return null; + + // find the service offering + $code = 'jumpstart_warranty_fee'; + $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + + // check if service is null. If null, return null + if ($service == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $service->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + protected function getServiceTitle() { $title = 'Service - Troubleshooting fee'; diff --git a/src/InvoiceRule/Overheat.php b/src/InvoiceRule/Overheat.php index 4c06bddb..af0ce182 100644 --- a/src/InvoiceRule/Overheat.php +++ b/src/InvoiceRule/Overheat.php @@ -10,14 +10,19 @@ use App\Ramcar\ServiceType; use App\Entity\ServiceOffering; use App\Entity\CustomerVehicle; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; class Overheat implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -29,13 +34,22 @@ class Overheat implements InvoiceRuleInterface { $stype = $criteria->getServiceType(); $has_coolant = $criteria->hasCoolant(); + $pt_id = $criteria->getPriceTier(); $items = []; if ($stype == $this->getID()) { $cv = $criteria->getCustomerVehicle(); - $fee = $this->getServiceTypeFee($cv); + + // check if price tier has item price + $pt_price = $this->getPriceTierItemPrice($pt_id, $cv); + + if ($pt_price == null) + $price = $this->getServiceTypeFee($cv); + else + + $price = $pt_price; // add the service fee to items $qty = 1; @@ -43,10 +57,10 @@ class Overheat implements InvoiceRuleInterface 'service_type' => $this->getID(), 'qty' => $qty, 'title' => $this->getServiceTitle(), - 'price' => $fee, + 'price' => $price, ]; - $qty_fee = bcmul($qty, $fee, 2); + $qty_fee = bcmul($qty, $price, 2); $total_price = $qty_fee; if ($has_coolant) @@ -112,6 +126,39 @@ class Overheat implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id, CustomerVehicle $cv) + { + // price_tier is default + if ($pt_id == 0) + return null; + + // find the item type for service offering + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + if ($item_type == null) + return null; + + // find the service offering + $code = 'overheat_fee'; + + // check if customer vehicle has a motolite battery + // if yes, set the code to the motolite user service fee + if ($cv->hasMotoliteBattery()) + $code = 'motolite_user_service_fee'; + + $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + + // check if service is null. If null, return null + if ($service == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $service->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + protected function getServiceTitle() { $title = 'Service - ' . ServiceType::getName(ServiceType::OVERHEAT_ASSISTANCE); diff --git a/src/InvoiceRule/PostRecharged.php b/src/InvoiceRule/PostRecharged.php index 808f2340..b0b20995 100644 --- a/src/InvoiceRule/PostRecharged.php +++ b/src/InvoiceRule/PostRecharged.php @@ -7,14 +7,19 @@ use Doctrine\ORM\EntityManagerInterface; use App\InvoiceRuleInterface; use App\Entity\ServiceOffering; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; class PostRecharged implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -25,22 +30,29 @@ class PostRecharged implements InvoiceRuleInterface public function compute($criteria, &$total) { $stype = $criteria->getServiceType(); + $pt_id = $criteria->getPriceTier(); $items = []; if ($stype == $this->getID()) { - $fee = $this->getServiceTypeFee(); + // check if price tier has item price + $pt_price = $this->getPriceTierItemPrice($pt_id); + + if ($pt_price == null) + $price = $this->getServiceTypeFee(); + else + $price = $pt_price; $qty = 1; $items[] = [ 'service_type' => $this->getID(), 'qty' => $qty, 'title' => $this->getServiceTitle(), - 'price' => $fee, + 'price' => $price, ]; - $qty_price = bcmul($fee, $qty, 2); + $qty_price = bcmul($price, $qty, 2); $total['total_price'] = bcadd($total['total_price'], $qty_price, 2); } @@ -72,6 +84,33 @@ class PostRecharged implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id) + { + // price_tier is default + if ($pt_id == 0) + return null; + + // find the item type for service offering + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + if ($item_type == null) + return null; + + // find the service offering + $code = 'post_recharged_fee'; + $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + + // check if service is null. If null, return null + if ($service == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $service->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + protected function getServiceTitle() { $title = 'Recharge fee'; diff --git a/src/InvoiceRule/PostReplacement.php b/src/InvoiceRule/PostReplacement.php index aba6d9aa..774b61de 100644 --- a/src/InvoiceRule/PostReplacement.php +++ b/src/InvoiceRule/PostReplacement.php @@ -7,14 +7,19 @@ use Doctrine\ORM\EntityManagerInterface; use App\InvoiceRuleInterface; use App\Entity\ServiceOffering; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; class PostReplacement implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -25,22 +30,29 @@ class PostReplacement implements InvoiceRuleInterface public function compute($criteria, &$total) { $stype = $criteria->getServiceType(); + $pt_id = $criteria->getPriceTier(); $items = []; if ($stype == $this->getID()) { - $fee = $this->getServiceTypeFee(); + // check if price tier has item price + $pt_price = $this->getPriceTierItemPrice($pt_id); + + if ($pt_price == null) + $price = $this->getServiceTypeFee(); + else + $price = $pt_price; $qty = 1; $items[] = [ 'service_type' => $this->getID(), 'qty' => $qty, 'title' => $this->getServiceTitle(), - 'price' => $fee, + 'price' => $price, ]; - $qty_price = bcmul($fee, $qty, 2); + $qty_price = bcmul($price, $qty, 2); $total['total_price'] = bcadd($total['total_price'], $qty_price, 2); } @@ -71,6 +83,33 @@ class PostReplacement implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id) + { + // price_tier is default + if ($pt_id == 0) + return null; + + // find the item type for service offering + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + if ($item_type == null) + return null; + + // find the service offering + $code = 'post_replacement_fee'; + $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + + // check if service is null. If null, return null + if ($service == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $service->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + protected function getServiceTitle() { $title = 'Battery replacement'; diff --git a/src/InvoiceRule/PriceTier.php b/src/InvoiceRule/PriceTier.php deleted file mode 100644 index 7db80ccc..00000000 --- a/src/InvoiceRule/PriceTier.php +++ /dev/null @@ -1,220 +0,0 @@ -em = $em; - } - - public function getID() - { - return 'price_tier'; - } - - public function compute($criteria, &$total) - { - $pt_id = $criteria->getPriceTier(); - - // get the service type - $service_type = $criteria->getServiceType(); - - // get price tier - $pt = $em->getRepository(PTEntity::class)->find($pt_id); - - // price tier is default - if ($pt == null) - { - // check if service type is battery sales and battery warranty (sometimes they add a battery - // for battery warranty - if (($service_type == ServiceType::BATTERY_REPLACEMENT_NEW) || - ($service_type == ServiceType::BATTERY_REPLACEMENT_WARRANTY)) - { - $items = $this->processBatteryEntries($criteria, $total); - } - else - { - // TODO: process the service fees - $items = $this->processServiceEntries($criteria, $total); - } - } - else - { - // get items in price tier - $pt_items = $pt->getItemPrices(); - - foreach ($pt_items as $pt_item) - { - // make item price hash - $pt_prices[$pt_item->getItemID()] = $pt_item->getPrice(); - } - - // TODO: finish this - } - - return $items; - } - - public function validatePromo($criteria, $promo_id) - { - return false; - } - - 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) && - ($stype != ServiceType::BATTERY_REPLACEMENT_WARRANTY)) - 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) - { - $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; - - // 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; - } - - protected function processBatteryEntries($criteria, &$total) - { - $items = []; - - // get the entries - $entries = $criteria->getEntries(); - foreach($entries as $entry) - { - $batt = $entry['battery']; - $qty = $entry['qty']; - $trade_in = null; - - if (isset($entry['trade_in'])) - $trade_in = $entry['trade_in']; - - $size = $batt->getSize(); - - if ($trade_in == null) - { - // check if battery purchase or battery replacement - if ($service_type == ServiceType::BATTERY_REPLACEMENT_NEW) - $price = $batt->getSellingPrice(); - else - $price = 0; - - $items[] = [ - 'service_type' => $this->getID(), - 'battery' => $batt, - 'qty' => $qty, - 'title' => $this->getItemTitle($criteria->getServiceType(), $batt), - 'price' => $price, - ]; - - $qty_price = bcmul($price, $qty, 2); - - $total['sell_price'] = bcadd($total['sell_price'], $qty_price, 2); - $total['total_price'] = bcadd($total['total_price'], $qty_price, 2); - } - } - - return $items; - } - - protected function processServiceEntries($criteria, &$total) - { - } - - protected function getItemTitle($service_type, $battery) - { - $title =''; - - // TODO: check for service type - switch ($service_type) { - case (ServiceType::BATTERY_REPLACEMENT_NEW): - $title = $battery->getModel()->getName() . ' ' . $battery->getSize()->getName(); - break; - case (ServiceType::BATTERY_REPLACEMENT_WARRANTY): - $title = $battery->getModel()->getName() . ' ' . $battery->getSize()->getName() . ' - Service Unit'; - break; - default: - $title = ''; - break; - } - - return $title; - - protected function getServiceTitle($service_type, $fuel_type) - { - $title = ''; - - switch ($service_type) { - case (ServiceType::JUMPSTART_TROUBLESHOOT): - case (ServiceType::JUMPSTART_WARRANTY): - $title = 'Service - Troubleshooting fee'; - break; - case (ServiceType::OVERHEAT_ASSISTANCE): - $title = 'Service - ' . ServiceType::getName(ServiceType::OVERHEAT_ASSISTANCE); - break; - case (ServiceType::POST_RECHARGED): - $title = 'Recharge fee'; - break; - case (ServiceType::POST_REPLACEMENT): - $title = 'Battery replacement'; - break; - case (ServiceType::TIRE_REPAIR): - $title = 'Service - Flat Tire'; - break; - case (ServiceType::EMERGENCY_REFUEL): - $title = '4L - ' . ucfirst($fuel_type); - break; - default: - $title = ''; - } - - return $title; - } - - protected function getServiceCoolantTitle() - { - $title = '4L Coolant'; - - return $title; - } - -} diff --git a/src/InvoiceRule/TireRepair.php b/src/InvoiceRule/TireRepair.php index 755c11bd..96d3c525 100644 --- a/src/InvoiceRule/TireRepair.php +++ b/src/InvoiceRule/TireRepair.php @@ -8,14 +8,19 @@ use App\InvoiceRuleInterface; use App\Entity\ServiceOffering; use App\Entity\CustomerVehicle; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; class TireRepair implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -26,13 +31,21 @@ class TireRepair implements InvoiceRuleInterface public function compute($criteria, &$total) { $stype = $criteria->getServiceType(); + $pt_id = $criteria->getPriceTier(); $items = []; if ($stype == $this->getID()) { $cv = $criteria->getCustomerVehicle(); - $fee = $this->getServiceTypeFee($cv); + + // check if price tier has item price + $pt_price = $this->getPriceTierItemPrice($pt_id, $cv); + + if ($pt_price == null) + $price = $this->getServiceTypeFee($cv); + else + $price = $pt_price; // add the service fee to items $qty = 1; @@ -40,10 +53,10 @@ class TireRepair implements InvoiceRuleInterface 'service_type' => $this->getID(), 'qty' => $qty, 'title' => $this->getServiceTitle(), - 'price' => $fee, + 'price' => $price, ]; - $qty_price = bcmul($fee, $qty, 2); + $qty_price = bcmul($price, $qty, 2); $total['total_price'] = bcadd($total['total_price'], $qty_price, 2); } @@ -79,6 +92,39 @@ class TireRepair implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id, CustomerVehicle $cv) + { + // price_tier is default + if ($pt_id == 0) + return null; + + // find the item type for service offering + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']); + if ($item_type == null) + return null; + + // find the service offering + $code = 'tire_repair_fee'; + + // check if customer vehicle has a motolite battery + // if yes, set the code to the motolite user service fee + if ($cv->hasMotoliteBattery()) + $code = 'motolite_user_service_fee'; + + $service = $this->em->getRepository(ServiceOffering::class)->findOneBy(['code' => $code]); + + // check if service is null. If null, return null + if ($service == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $service->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + protected function getServiceTitle() { $title = 'Service - Flat Tire'; diff --git a/src/Service/InvoiceManager.php b/src/Service/InvoiceManager.php index 6e37a207..f46c95ab 100644 --- a/src/Service/InvoiceManager.php +++ b/src/Service/InvoiceManager.php @@ -47,13 +47,13 @@ class InvoiceManager implements InvoiceGeneratorInterface return [ new InvoiceRule\BatterySales($this->em, $this->pt_manager), new InvoiceRule\BatteryReplacementWarranty($this->em, $this->pt_manager), - new InvoiceRule\Jumpstart($this->em), - new InvoiceRule\JumpstartWarranty($this->em), - new InvoiceRule\PostRecharged($this->em), - new InvoiceRule\PostReplacement($this->em), - new InvoiceRule\Overheat($this->em), - new InvoiceRule\Fuel($this->em), - new InvoiceRule\TireRepair($this->em), + new InvoiceRule\Jumpstart($this->em, $this->pt_manager), + new InvoiceRule\JumpstartWarranty($this->em, $this->pt_manager), + new InvoiceRule\PostRecharged($this->em, $this->pt_manager), + new InvoiceRule\PostReplacement($this->em, $this->pt_manager), + new InvoiceRule\Overheat($this->em, $this->pt_manager), + 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\Tax($this->em), From 57fd7fe5ac1e3c49e7e608d6b5c71556f3a46c2c Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Thu, 25 Jan 2024 02:23:47 -0500 Subject: [PATCH 22/29] Fix issues found during testing. #782 --- src/InvoiceRule/Fuel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InvoiceRule/Fuel.php b/src/InvoiceRule/Fuel.php index 691a1ba2..e4f365b9 100644 --- a/src/InvoiceRule/Fuel.php +++ b/src/InvoiceRule/Fuel.php @@ -158,7 +158,7 @@ class Fuel implements InvoiceRuleInterface return null; } - protected function getPriceTierItemPrice($pt_id) + protected function getPriceTierItemPrice($pt_id, CustomerVehicle $cv) { // price_tier is default if ($pt_id == 0) From c136b0666ba4f3601e9db88baa0441079cc72f3f Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Thu, 25 Jan 2024 03:08:00 -0500 Subject: [PATCH 23/29] Fix issues found during testing. #782 --- src/InvoiceRule/Tax.php | 35 +++++++++++++++++-- src/Service/InvoiceManager.php | 2 +- .../JobOrderHandler/ResqJobOrderHandler.php | 10 ++++-- src/Service/PriceTierManager.php | 25 ++++++++++--- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/InvoiceRule/Tax.php b/src/InvoiceRule/Tax.php index b1e6a600..50834e44 100644 --- a/src/InvoiceRule/Tax.php +++ b/src/InvoiceRule/Tax.php @@ -9,14 +9,19 @@ use App\InvoiceRuleInterface; use App\Ramcar\ServiceType; use App\Entity\ServiceOffering; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; class Tax implements InvoiceRuleInterface { protected $em; + protected $pt_manager; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->em = $em; + $this->pt_manager = $pt_manager; } public function getID() @@ -40,6 +45,7 @@ class Tax implements InvoiceRuleInterface // compute tax per item if service type is battery sales $stype = $criteria->getServiceType(); + $pt = $criteria->getPriceTier(); if ($stype == ServiceType::BATTERY_REPLACEMENT_NEW) { @@ -58,7 +64,13 @@ class Tax implements InvoiceRuleInterface $battery = $entry['battery']; $qty = $entry['qty']; - $price = $battery->getSellingPrice(); + // check if price tier has item price for battery + $pt_price = $this->getPriceTierItemPrice($pt, $battery); + + if ($pt_price == null) + $price = $battery->getSellingPrice(); + else + $price = $pt_price; $vat = $this->getTaxAmount($price, $tax_rate); @@ -96,6 +108,25 @@ class Tax implements InvoiceRuleInterface return null; } + protected function getPriceTierItemPrice($pt_id, $batt) + { + // price tier is default + if ($pt_id == 0) + return null; + + // find the item type battery + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']); + if ($item_type == null) + return null; + + $item_type_id = $item_type->getID(); + $item_id = $batt->getID(); + + $price = $this->pt_manager->getItemPrice($pt_id, $item_type_id, $item_id); + + return $price; + } + protected function getTaxAmount($price, $tax_rate) { $vat_ex_price = $this->getTaxExclusivePrice($price, $tax_rate); diff --git a/src/Service/InvoiceManager.php b/src/Service/InvoiceManager.php index f46c95ab..8f655224 100644 --- a/src/Service/InvoiceManager.php +++ b/src/Service/InvoiceManager.php @@ -56,7 +56,7 @@ class InvoiceManager implements InvoiceGeneratorInterface new InvoiceRule\TireRepair($this->em, $this->pt_manager), new InvoiceRule\DiscountType($this->em), new InvoiceRule\TradeIn(), - new InvoiceRule\Tax($this->em), + new InvoiceRule\Tax($this->em, $this->pt_manager), ]; } diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 28e9035d..c7c38100 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -588,7 +588,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface { $source = $jo->getSource(); - // TODO: set the price tier according to location. + // get the price tier according to location. $price_tier = $this->pt_manager->getPriceTier($jo->getCoordinates()); $this->ic->generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source, $price_tier, $error_array); } @@ -822,7 +822,9 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface { $source = $obj->getSource(); - $this->ic->generateInvoiceCriteria($obj, $promo_id, $invoice_items, $source, $error_array); + // get the price tier according to location. + $price_tier = $this->pt_manager->getPriceTier($obj->getCoordinates()); + $this->ic->generateInvoiceCriteria($obj, $promo_id, $invoice_items, $source, $price_tier, $error_array); } // validate @@ -2170,7 +2172,9 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // NOTE: this is CMB code but for compilation purposes we need to add this $source = $jo->getSource(); - $this->ic->generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source, $error_array); + // get the price tier according to location. + $price_tier = $this->pt_manager->getPriceTier($jo->getCoordinates()); + $this->ic->generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source, $price_tier, $error_array); } // validate diff --git a/src/Service/PriceTierManager.php b/src/Service/PriceTierManager.php index c572af76..1d8bb60c 100644 --- a/src/Service/PriceTierManager.php +++ b/src/Service/PriceTierManager.php @@ -51,10 +51,27 @@ class PriceTierManager return $actual_price; } - public function getPriceTier(Point $point) + public function getPriceTier(Point $coordinates) { - // TODO: get location's price tier, given a set of coordinates - // for now, hardcoded for testing purposes - return 1; + $price_tier_id = 0; + + $long = $coordinates->getLongitude(); + $lat = $coordinates->getLatitude(); + + // get location's price tier, given a set of coordinates + $query = $this->em->createQuery('SELECT s from App\Entity\SupportedArea s where st_contains(s.coverage_area, point(:long, :lat)) = true'); + $area = $query->setParameter('long', $long) + ->setParameter('lat', $lat) + ->setMaxResults(1) + ->getOneOrNullResult(); + + if ($area != null) + { + $price_tier = $area->getPriceTier(); + if ($price_tier != null) + $price_tier_id = $price_tier->getID(); + } + + return $price_tier_id; } } From 213171f4b7e2bd07a65dd826cd0983d2a04b9ab4 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Fri, 26 Jan 2024 04:44:13 -0500 Subject: [PATCH 24/29] Add price tiering for Resq 2 App and TAPI. #782 --- src/Controller/APIController.php | 5 +++ src/Controller/CAPI/RiderAppController.php | 7 ++- .../CustomerAppAPI/InvoiceController.php | 45 ++++++++++++++++++- .../CustomerAppAPI/JobOrderController.php | 15 ++++++- src/Controller/TAPI/JobOrderController.php | 16 ++++++- src/Service/PriceTierManager.php | 31 +++++++------ 6 files changed, 99 insertions(+), 20 deletions(-) diff --git a/src/Controller/APIController.php b/src/Controller/APIController.php index 16a9cdf5..0eb148c5 100644 --- a/src/Controller/APIController.php +++ b/src/Controller/APIController.php @@ -50,6 +50,7 @@ use App\Service\HubFilterLogger; use App\Service\HubFilteringGeoChecker; use App\Service\HashGenerator; use App\Service\JobOrderManager; +use App\Service\PriceTierManager; use App\Entity\MobileSession; use App\Entity\Customer; @@ -2911,6 +2912,10 @@ class APIController extends Controller implements LoggedController // old app doesn't have separate jumpstart $icrit->setSource(TransactionOrigin::CALL); + // set price tier + $pt_id = $this->pt_manager->getPriceTier($jo->getCoordinates()); + $icrit->setPriceTier($pt_id); + // check promo $promo_id = $req->request->get('promo_id'); if (!empty($promo_id)) diff --git a/src/Controller/CAPI/RiderAppController.php b/src/Controller/CAPI/RiderAppController.php index d29f6a04..6cce3745 100644 --- a/src/Controller/CAPI/RiderAppController.php +++ b/src/Controller/CAPI/RiderAppController.php @@ -34,6 +34,7 @@ use App\Service\JobOrderHandlerInterface; use App\Service\InvoiceGeneratorInterface; use App\Service\RisingTideGateway; use App\Service\RiderTracker; +use App\Service\PriceTierManager; use App\Ramcar\ServiceType; use App\Ramcar\TradeInType; @@ -1099,7 +1100,7 @@ class RiderAppController extends ApiController return new APIResponse(true, 'Batteries found.', $data); } - public function changeService(Request $req, EntityManagerInterface $em, InvoiceGeneratorInterface $ic) + public function changeService(Request $req, EntityManagerInterface $em, InvoiceGeneratorInterface $ic, PriceTierManager $pt_manager) { // $this->debugRequest($req); @@ -1203,6 +1204,10 @@ class RiderAppController extends ApiController $crit->setHasCoolant($jo->hasCoolant()); $crit->setIsTaxable(); + // set price tier + $pt_id = $pt_manager->getPriceTier($jo->getCoordinates()); + $icrit->setPriceTier($pt_id); + if ($promo != null) $crit->addPromo($promo); diff --git a/src/Controller/CustomerAppAPI/InvoiceController.php b/src/Controller/CustomerAppAPI/InvoiceController.php index a5c3a8b8..14f1a87e 100644 --- a/src/Controller/CustomerAppAPI/InvoiceController.php +++ b/src/Controller/CustomerAppAPI/InvoiceController.php @@ -4,18 +4,22 @@ namespace App\Controller\CustomerAppAPI; use Symfony\Component\HttpFoundation\Request; use Catalyst\ApiBundle\Component\Response as ApiResponse; +use CrEOF\Spatial\PHP\Types\Geometry\Point; use App\Service\InvoiceGeneratorInterface; +use App\Service\PriceTierManager; use App\Ramcar\InvoiceCriteria; use App\Ramcar\TradeInType; use App\Ramcar\TransactionOrigin; use App\Entity\CustomerVehicle; use App\Entity\Promo; use App\Entity\Battery; +use App\Entity\Customer; +use App\Entity\CustomerMetadata; class InvoiceController extends ApiController { - public function getEstimate(Request $req, InvoiceGeneratorInterface $ic) + public function getEstimate(Request $req, InvoiceGeneratorInterface $ic, PriceTierManager $pt_manager) { // $this->debugRequest($req); @@ -36,6 +40,9 @@ class InvoiceController extends ApiController return new ApiResponse(false, 'No customer information found.'); } + // get customer location from customer_metadata using customer id + $coordinates = $this->getCustomerMetadata($cust); + // make invoice criteria $icrit = new InvoiceCriteria(); $icrit->setServiceType($req->request->get('service_type')); @@ -113,6 +120,18 @@ class InvoiceController extends ApiController // set JO source $icrit->setSource(TransactionOrigin::MOBILE_APP); + // set price tier + $pt_id = 0; + if ($coordinates != null) + { + error_log('coordinates are not null'); + $pt_id = $pt_manager->getPriceTier($coordinates); + } + else + error_log('null?'); + + $icrit->setPriceTier($pt_id); + // send to invoice generator $invoice = $ic->generateInvoice($icrit); @@ -148,4 +167,28 @@ class InvoiceController extends ApiController // response return new ApiResponse(true, '', $data); } + + protected function getCustomerMetadata(Customer $cust) + { + $coordinates = null; + + // check if customer already has existing metadata + $c_meta = $this->em->getRepository(CustomerMetadata::class)->findOneBy(['customer' => $cust]); + if ($c_meta != null) + { + $meta_data = $c_meta->getAllMetaInfo(); + foreach ($meta_data as $m_info) + { + if ((isset($m_info['longitude'])) && (isset($m_info['latitude']))) + { + $lng = $m_info['longitude']; + $lat = $m_info['latitude']; + + $coordinates = new Point($lng, $lat); + } + } + } + + return $coordinates; + } } diff --git a/src/Controller/CustomerAppAPI/JobOrderController.php b/src/Controller/CustomerAppAPI/JobOrderController.php index 143f2a2e..fa5a9a85 100644 --- a/src/Controller/CustomerAppAPI/JobOrderController.php +++ b/src/Controller/CustomerAppAPI/JobOrderController.php @@ -21,6 +21,7 @@ use App\Service\HubDistributor; use App\Service\HubFilterLogger; use App\Service\HubFilteringGeoChecker; use App\Service\JobOrderManager; +use App\Service\PriceTierManager; use App\Ramcar\ServiceType; use App\Ramcar\APIRiderStatus; use App\Ramcar\InvoiceCriteria; @@ -484,7 +485,8 @@ class JobOrderController extends ApiController HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger, HubFilteringGeoChecker $hub_geofence, - JobOrderManager $jo_manager + JobOrderManager $jo_manager, + PriceTierManager $pt_manager ) { // validate params $validity = $this->validateRequest($req, [ @@ -698,6 +700,10 @@ class JobOrderController extends ApiController // set JO source $icrit->setSource(TransactionOrigin::MOBILE_APP); + // set price tier + $pt_id = $pt_manager->getPriceTier($jo->getCoordinates()); + $icrit->setPriceTier($pt_id); + // send to invoice generator $invoice = $ic->generateInvoice($icrit); $jo->setInvoice($invoice); @@ -970,7 +976,8 @@ class JobOrderController extends ApiController HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger, HubFilteringGeoChecker $hub_geofence, - JobOrderManager $jo_manager + JobOrderManager $jo_manager, + PriceTierManager $pt_manager ) { // validate params $validity = $this->validateRequest($req, [ @@ -1127,6 +1134,10 @@ class JobOrderController extends ApiController // set JO source $icrit->setSource(TransactionOrigin::MOBILE_APP); + // set price tier + $pt_id = $pt_manager->getPriceTier($jo->getCoordinates()); + $icrit->setPriceTier($pt_id); + // send to invoice generator $invoice = $ic->generateInvoice($icrit); $jo->setInvoice($invoice); diff --git a/src/Controller/TAPI/JobOrderController.php b/src/Controller/TAPI/JobOrderController.php index f7541b50..ae4d5a61 100644 --- a/src/Controller/TAPI/JobOrderController.php +++ b/src/Controller/TAPI/JobOrderController.php @@ -46,6 +46,7 @@ use App\Service\RiderTracker; use App\Service\PromoLogger; use App\Service\MapTools; use App\Service\JobOrderManager; +use App\Service\PriceTierManager; use App\Entity\JobOrder; use App\Entity\CustomerVehicle; @@ -79,7 +80,8 @@ class JobOrderController extends ApiController FCMSender $fcmclient, RiderAssignmentHandlerInterface $rah, PromoLogger $promo_logger, HubSelector $hub_select, HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger, - HubFilteringGeoChecker $hub_geofence, EntityManagerInterface $em, JobOrderManager $jo_manager) + HubFilteringGeoChecker $hub_geofence, EntityManagerInterface $em, JobOrderManager $jo_manager, + PriceTierManager $pt_manager) { $this->denyAccessUnlessGranted('tapi_jo.request', null, 'No access.'); @@ -165,7 +167,17 @@ class JobOrderController extends ApiController // set JO source $icrit->setSource(TransactionOrigin::THIRD_PARTY); - $icrit->addEntry($data['batt'], $data['trade_in_type'], 1); + // set price tier + $pt_id = $pt_manager->getPriceTier($jo->getCoordinates()); + $icrit->setPriceTier($pt_id); + + // add the actual battery item first + $icrit->addEntry($data['batt'], null, 1); + + // if we have a trade in, add it as well, assuming trade in battery == battery purchased + if (!empty($data['trade_in_type'])) { + $icrit->addEntry($data['batt'], $data['trade_in_type'], 1); + } // send to invoice generator $invoice = $ic->generateInvoice($icrit); diff --git a/src/Service/PriceTierManager.php b/src/Service/PriceTierManager.php index 1d8bb60c..62d657f4 100644 --- a/src/Service/PriceTierManager.php +++ b/src/Service/PriceTierManager.php @@ -55,21 +55,24 @@ class PriceTierManager { $price_tier_id = 0; - $long = $coordinates->getLongitude(); - $lat = $coordinates->getLatitude(); - - // get location's price tier, given a set of coordinates - $query = $this->em->createQuery('SELECT s from App\Entity\SupportedArea s where st_contains(s.coverage_area, point(:long, :lat)) = true'); - $area = $query->setParameter('long', $long) - ->setParameter('lat', $lat) - ->setMaxResults(1) - ->getOneOrNullResult(); - - if ($area != null) + if ($coordinates != null) { - $price_tier = $area->getPriceTier(); - if ($price_tier != null) - $price_tier_id = $price_tier->getID(); + $long = $coordinates->getLongitude(); + $lat = $coordinates->getLatitude(); + + // get location's price tier, given a set of coordinates + $query = $this->em->createQuery('SELECT s from App\Entity\SupportedArea s where st_contains(s.coverage_area, point(:long, :lat)) = true'); + $area = $query->setParameter('long', $long) + ->setParameter('lat', $lat) + ->setMaxResults(1) + ->getOneOrNullResult(); + + if ($area != null) + { + $price_tier = $area->getPriceTier(); + if ($price_tier != null) + $price_tier_id = $price_tier->getID(); + } } return $price_tier_id; From 20f5bb08e02e16d26085d3a75856b1f0b1af0f22 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Fri, 26 Jan 2024 04:50:54 -0500 Subject: [PATCH 25/29] Add checking for longitude and latitude when calling getEstimate. #782 --- src/Controller/CustomerAppAPI/InvoiceController.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Controller/CustomerAppAPI/InvoiceController.php b/src/Controller/CustomerAppAPI/InvoiceController.php index 14f1a87e..2e9779eb 100644 --- a/src/Controller/CustomerAppAPI/InvoiceController.php +++ b/src/Controller/CustomerAppAPI/InvoiceController.php @@ -41,7 +41,16 @@ class InvoiceController extends ApiController } // get customer location from customer_metadata using customer id - $coordinates = $this->getCustomerMetadata($cust); + $lng = $req->request->get('longitude'); + $lat = $req->request->get('latitude'); + + if ((empty($lng)) || (empty($lat))) + { + // use customer metadata location as basis + $coordinates = $this->getCustomerMetadata($cust); + } + else + $coordinates = new Point($lng, $lat); // make invoice criteria $icrit = new InvoiceCriteria(); From d4eae00902393cd67cad6d5c1deab5d7970fe65d Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Sun, 28 Jan 2024 22:21:34 -0500 Subject: [PATCH 26/29] Add sql for item types. #782 --- utils/item_types/item_types.sql | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 utils/item_types/item_types.sql diff --git a/utils/item_types/item_types.sql b/utils/item_types/item_types.sql new file mode 100644 index 00000000..7c5aeeba --- /dev/null +++ b/utils/item_types/item_types.sql @@ -0,0 +1,53 @@ +-- MySQL dump 10.19 Distrib 10.3.39-MariaDB, for Linux (x86_64) +-- +-- Host: localhost Database: resq +-- ------------------------------------------------------ +-- Server version 10.3.39-MariaDB + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `item_type` +-- + +DROP TABLE IF EXISTS `item_type`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `item_type` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(80) NOT NULL, + `code` varchar(80) NOT NULL, + PRIMARY KEY (`id`), + KEY `item_type_idx` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `item_type` +-- + +LOCK TABLES `item_type` WRITE; +/*!40000 ALTER TABLE `item_type` DISABLE KEYS */; +INSERT INTO `item_type` VALUES (1,'Battery','battery'),(2,'Service Offering','service_offering'); +/*!40000 ALTER TABLE `item_type` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2024-01-28 20:59:44 From 4d89e7420f142cdd3323bd3dfd8d570506848067 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Tue, 20 Feb 2024 02:09:57 -0500 Subject: [PATCH 27/29] Add checking for location and price tier when getting list of compatible batteries. #780 --- config/routes/tapi.yaml | 2 +- .../CustomerAppAPI/VehicleController.php | 41 +++++++++++++++-- src/Controller/TAPI/BatteryController.php | 44 +++++++++++++++++-- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/config/routes/tapi.yaml b/config/routes/tapi.yaml index afa2f084..5bf1a503 100644 --- a/config/routes/tapi.yaml +++ b/config/routes/tapi.yaml @@ -51,7 +51,7 @@ tapi_vehicle_make_list: tapi_battery_list: path: /tapi/vehicles/{vid}/compatible_batteries controller: App\Controller\TAPI\BatteryController::getCompatibleBatteries - methods: [GET] + methods: [POST] # promos tapi_promo_list: diff --git a/src/Controller/CustomerAppAPI/VehicleController.php b/src/Controller/CustomerAppAPI/VehicleController.php index ba76d7a4..c8fd15e9 100644 --- a/src/Controller/CustomerAppAPI/VehicleController.php +++ b/src/Controller/CustomerAppAPI/VehicleController.php @@ -4,16 +4,19 @@ namespace App\Controller\CustomerAppAPI; use Symfony\Component\HttpFoundation\Request; use Catalyst\ApiBundle\Component\Response as ApiResponse; +use CrEOF\Spatial\PHP\Types\Geometry\Point; use App\Entity\CustomerVehicle; use App\Entity\JobOrder; use App\Entity\VehicleManufacturer; use App\Entity\Vehicle; +use App\Entity\ItemType; use App\Ramcar\JOStatus; use App\Ramcar\ServiceType; use App\Ramcar\TradeInType; use App\Ramcar\InsuranceApplicationStatus; use App\Service\PayMongoConnector; +use App\Service\PriceTierManager; use DateTime; class VehicleController extends ApiController @@ -237,7 +240,7 @@ class VehicleController extends ApiController ]); } - public function getCompatibleBatteries(Request $req, $vid) + public function getCompatibleBatteries(Request $req, $vid, PriceTierManager $pt_manager) { // validate params $validity = $this->validateRequest($req); @@ -252,11 +255,43 @@ class VehicleController extends ApiController return new ApiResponse(false, 'Invalid vehicle.'); } + // get location from request + $lng = $req->query->get('longitude', ''); + $lat = $req->query->get('latitude', ''); + + $batts = $vehicle->getActiveBatteries(); + $pt_id = 0; + if ((!(empty($lng))) && (!(empty($lat)))) + { + // get the price tier + $coordinates = new Point($lng, $lat); + + $pt_id = $pt_manager->getPriceTier($coordinates); + } + // batteries $batt_list = []; - $batts = $vehicle->getActiveBatteries(); foreach ($batts as $batt) { // TODO: Add warranty_tnv to battery information + // check if customer location is in a price tier location + if ($pt_id == 0) + $price = $batt->getSellingPrice(); + else + { + // get item type for battery + $item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']); + if ($item_type == null) + $price = $batt->getSellingPrice(); + else + { + $item_type_id = $item_type->getID(); + $batt_id = $batt->getID(); + + // find the item price given price tier id and battery id + $price = $pt_manager->getItemPrice($pt_id, $item_type_id, $batt_id); + } + } + $batt_list[] = [ 'id' => $batt->getID(), 'mfg_id' => $batt->getManufacturer()->getID(), @@ -265,7 +300,7 @@ class VehicleController extends ApiController 'model_name' => $batt->getModel()->getName(), 'size_id' => $batt->getSize()->getID(), 'size_name' => $batt->getSize()->getName(), - 'price' => $batt->getSellingPrice(), + 'price' => $price, 'wty_private' => $batt->getWarrantyPrivate(), 'wty_commercial' => $batt->getWarrantyCommercial(), 'image_url' => $this->getBatteryImageURL($req, $batt), diff --git a/src/Controller/TAPI/BatteryController.php b/src/Controller/TAPI/BatteryController.php index 0705a3ab..0d38dd5f 100644 --- a/src/Controller/TAPI/BatteryController.php +++ b/src/Controller/TAPI/BatteryController.php @@ -13,6 +13,11 @@ use Catalyst\ApiBundle\Component\Response as APIResponse; use App\Ramcar\APIResult; use App\Entity\Vehicle; +use App\Entity\ItemType; + +use App\Service\PriceTierManager; + +use CrEOF\Spatial\PHP\Types\Geometry\Point; use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator; @@ -25,7 +30,7 @@ class BatteryController extends ApiController $this->acl_gen = $acl_gen; } - public function getCompatibleBatteries(Request $req, $vid, EntityManagerInterface $em) + public function getCompatibleBatteries(Request $req, $vid, EntityManagerInterface $em, PriceTierManager $pt_manager) { $this->denyAccessUnlessGranted('tapi_battery_compatible.list', null, 'No access.'); @@ -43,13 +48,44 @@ class BatteryController extends ApiController return new APIResponse(false, $message); } + // get location from request + $lng = $req->request->get('longitude', ''); + $lat = $req->request->get('latitude', ''); + + $batts = $vehicle->getActiveBatteries(); + $pt_id = 0; + if ((!(empty($lng))) && (!(empty($lat)))) + { + // get the price tier + $coordinates = new Point($lng, $lat); + + $pt_id = $pt_manager->getPriceTier($coordinates); + } + // batteries $batt_list = []; - // $batts = $vehicle->getBatteries(); - $batts = $vehicle->getActiveBatteries(); foreach ($batts as $batt) { // TODO: Add warranty_tnv to battery information + // check if customer location is in a price tier location + if ($pt_id == 0) + $price = $batt->getSellingPrice(); + else + { + // get item type for battery + $item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']); + if ($item_type == null) + $price = $batt->getSellingPrice(); + else + { + $item_type_id = $item_type->getID(); + $batt_id = $batt->getID(); + + // find the item price given price tier id and battery id + $price = $pt_manager->getItemPrice($pt_id, $item_type_id, $batt_id); + } + } + $batt_list[] = [ 'id' => $batt->getID(), 'mfg_id' => $batt->getManufacturer()->getID(), @@ -58,7 +94,7 @@ class BatteryController extends ApiController 'model_name' => $batt->getModel()->getName(), 'size_id' => $batt->getSize()->getID(), 'size_name' => $batt->getSize()->getName(), - 'price' => $batt->getSellingPrice(), + 'price' => $price, 'wty_private' => $batt->getWarrantyPrivate(), 'wty_commercial' => $batt->getWarrantyCommercial(), 'image_url' => $this->getBatteryImageURL($req, $batt), From 78a43ae85cab59d4e9413df4f1160722892112f4 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 23 Feb 2024 16:53:04 +0800 Subject: [PATCH 28/29] Add insurance body types endpoint #791 --- config/routes/apiv2.yaml | 5 +++++ .../CustomerAppAPI/InsuranceController.php | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/config/routes/apiv2.yaml b/config/routes/apiv2.yaml index deb3b11b..dc603c9f 100644 --- a/config/routes/apiv2.yaml +++ b/config/routes/apiv2.yaml @@ -307,4 +307,9 @@ apiv2_insurance_application_create: apiv2_insurance_premiums_banner: path: /apiv2/insurance/premiums_banner controller: App\Controller\CustomerAppAPI\InsuranceController::getPremiumsBanner + methods: [GET] + +apiv2_insurance_body_types: + path: /apiv2/insurance/body_types + controller: App\Controller\CustomerAppAPI\InsuranceController::getBodyTypes methods: [GET] \ No newline at end of file diff --git a/src/Controller/CustomerAppAPI/InsuranceController.php b/src/Controller/CustomerAppAPI/InsuranceController.php index 8685060b..cc7832eb 100644 --- a/src/Controller/CustomerAppAPI/InsuranceController.php +++ b/src/Controller/CustomerAppAPI/InsuranceController.php @@ -307,6 +307,27 @@ class InsuranceController extends ApiController ]); } + public function getBodyTypes(Request $req) + { + // validate params + $validity = $this->validateRequest($req); + + if (!$validity['is_valid']) { + return new ApiResponse(false, $validity['error']); + } + + // TODO: if this changes often, make an entity and make it manageable on CRM + $body_types = [ + 'SEDAN' => 'Sedan', + 'SUV' => 'SUV', + 'TRUCK' => 'Truck', + ]; + + return new ApiResponse(true, '', [ + 'body_types' => $body_types, + ]); + } + protected function getLineType($mv_type_id, $vehicle_use_type, $is_public = false) { $line = ''; From 4dd8efd95a4f648a2811a08f3b659f5d3e398bb8 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 23 Feb 2024 18:39:21 +0800 Subject: [PATCH 29/29] Fix returned format of body types endpoint #791 --- .../CustomerAppAPI/InsuranceController.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Controller/CustomerAppAPI/InsuranceController.php b/src/Controller/CustomerAppAPI/InsuranceController.php index cc7832eb..9d302549 100644 --- a/src/Controller/CustomerAppAPI/InsuranceController.php +++ b/src/Controller/CustomerAppAPI/InsuranceController.php @@ -318,9 +318,18 @@ class InsuranceController extends ApiController // TODO: if this changes often, make an entity and make it manageable on CRM $body_types = [ - 'SEDAN' => 'Sedan', - 'SUV' => 'SUV', - 'TRUCK' => 'Truck', + [ + 'id' => 'SEDAN', + 'name' => 'Sedan', + ], + [ + 'id' => 'SUV', + 'name' => 'SUV', + ], + [ + 'id' => 'TRUCK', + 'name' => 'Truck', + ] ]; return new ApiResponse(true, '', [