Fix invoice data on open edit form #1729

Merged
korina.cordero merged 9 commits from 803-fix-invoice-data-disappearing-on-open-edit-form into master 2024-09-24 04:23:03 +00:00
11 changed files with 209 additions and 68 deletions

View file

@ -14,6 +14,7 @@ use App\Ramcar\TransactionOrigin;
use App\Entity\CustomerVehicle; use App\Entity\CustomerVehicle;
use App\Entity\Promo; use App\Entity\Promo;
use App\Entity\Battery; use App\Entity\Battery;
use App\Entity\BatterySize;
use App\Entity\Customer; use App\Entity\Customer;
use App\Entity\CustomerMetadata; use App\Entity\CustomerMetadata;
@ -119,7 +120,8 @@ class InvoiceController extends ApiController
if (!empty($trade_in_type) && !empty($trade_in_batt)) { if (!empty($trade_in_type) && !empty($trade_in_batt)) {
$ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt); $ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt);
if (!empty($ti_batt_obj)) { if (!empty($ti_batt_obj)) {
$icrit->addEntry($ti_batt_obj, $trade_in_type, 1); $ti_batt_size_obj = $ti_batt_obj->getSize();
$icrit->addTradeInEntry($ti_batt_size_obj, $trade_in_type, 1);
} }
} }

View file

@ -35,6 +35,7 @@ use App\Ramcar\WarrantyClass;
use App\Ramcar\HubCriteria; use App\Ramcar\HubCriteria;
use App\Ramcar\DeliveryStatus; use App\Ramcar\DeliveryStatus;
use App\Entity\Battery; use App\Entity\Battery;
use App\Entity\BatterySize;
use App\Entity\Hub; use App\Entity\Hub;
use App\Entity\Promo; use App\Entity\Promo;
use App\Entity\JOEvent; use App\Entity\JOEvent;
@ -696,7 +697,8 @@ class JobOrderController extends ApiController
if (!empty($trade_in_type) && !empty($trade_in_batt)) { if (!empty($trade_in_type) && !empty($trade_in_batt)) {
$ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt); $ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt);
if (!empty($ti_batt_obj)) { if (!empty($ti_batt_obj)) {
$icrit->addEntry($ti_batt_obj, $trade_in_type, 1); $ti_batt_size_obj = $ti_batt_obj->getSize();
$icrit->addTradeInEntry($ti_batt_size_obj, $trade_in_type, 1);
} }
} }
@ -1157,7 +1159,8 @@ class JobOrderController extends ApiController
if (!empty($trade_in_type) && !empty($trade_in_batt)) { if (!empty($trade_in_type) && !empty($trade_in_batt)) {
$ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt); $ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt);
if (!empty($ti_batt_obj)) { if (!empty($ti_batt_obj)) {
$icrit->addEntry($ti_batt_obj, $trade_in_type, 1); $battery_size = $ti_batt_obj->getSize();
$icrit->addTradeInEntry($battery_size, $trade_in_type, 1);
} }
} }

View file

@ -50,10 +50,24 @@ class InvoiceItem
*/ */
protected $battery; protected $battery;
// battery size for trade in items
/**
* @ORM\ManyToOne(targetEntity="BatterySize")
* @ORM\JoinColumn(name="battery_size_id", referencedColumnName="id")
*/
protected $battery_size;
// trade in type
/**
* @ORM\Column(type="string", length=20)
*/
protected $trade_in_type;
public function __construct() public function __construct()
{ {
$this->title = ''; $this->title = '';
$this->price = 0.0; $this->price = 0.0;
$this->trade_in_type = '';
} }
public function getID() public function getID()
@ -115,4 +129,26 @@ class InvoiceItem
{ {
return $this->battery; return $this->battery;
} }
public function setBatterySize(BatterySize $battery_size)
{
$this->battery_size = $battery_size;
return $this;
}
public function getBatterySize()
{
return $this->battery_size;
}
public function setTradeInType(string $trade_in_type)
{
$this->trade_in_type = $trade_in_type;
return $this;
}
public function getTradeInType()
{
return $this->trade_in_type;
}
} }

View file

@ -116,14 +116,12 @@ class BatteryReplacementWarranty implements InvoiceRuleInterface
$qty = $item['quantity']; $qty = $item['quantity'];
if ($qty < 1) if ($qty < 1)
continue; continue;
// if this is a trade in, add trade in if (empty($item['trade_in']))
if (!empty($item['trade_in']) && TradeInType::validate($item['trade_in'])) {
$trade_in = $item['trade_in'];
else
$trade_in = null; $trade_in = null;
$criteria->addEntry($battery, $trade_in, $qty);
$criteria->addEntry($battery, $trade_in, $qty); }
} }
} }

View file

@ -103,26 +103,24 @@ class BatterySales implements InvoiceRuleInterface
// check if this is a valid battery // check if this is a valid battery
foreach ($invoice_items as $item) foreach ($invoice_items as $item)
{ {
$battery = $this->em->getRepository(Battery::class)->find($item['battery']); if (isset($item['battery']))
if (empty($battery))
{ {
$error = 'Invalid battery specified.'; $battery = $this->em->getRepository(Battery::class)->find($item['battery']);
return $error;
}
// quantity if (empty($battery))
$qty = $item['quantity']; {
if ($qty < 1) $error = 'Invalid battery specified.';
continue; 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; $trade_in = null;
$criteria->addEntry($battery, $trade_in, $qty);
$criteria->addEntry($battery, $trade_in, $qty); }
} }
} }

View file

@ -2,12 +2,24 @@
namespace App\InvoiceRule; namespace App\InvoiceRule;
use Doctrine\ORM\EntityManagerInterface;
use App\InvoiceRuleInterface; use App\InvoiceRuleInterface;
use App\Ramcar\TradeInType; use App\Ramcar\TradeInType;
use App\Ramcar\ServiceType;
use App\Entity\BatterySize;
class TradeIn implements InvoiceRuleInterface class TradeIn implements InvoiceRuleInterface
{ {
protected $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function getID() public function getID()
{ {
return 'trade-in'; return 'trade-in';
@ -29,16 +41,7 @@ class TradeIn implements InvoiceRuleInterface
if ($trade_in_type != null) if ($trade_in_type != null)
{ {
// at this point, entry is a trade in $batt_size = $entry['battery_size'];
// need to check if battery (coming from CRM) is set
// or battery_size is set (coming from rider app)
if (isset($entry['battery']))
{
$battery = $entry['battery'];
$batt_size = $battery->getSize();
}
else
$batt_size = $entry['battery_size'];
$ti_rate = $this->getTradeInRate($batt_size, $trade_in_type); $ti_rate = $this->getTradeInRate($batt_size, $trade_in_type);
@ -50,6 +53,8 @@ class TradeIn implements InvoiceRuleInterface
$price = bcmul($ti_rate, -1, 2); $price = bcmul($ti_rate, -1, 2);
$items[] = [ $items[] = [
'battery_size' => $batt_size,
'trade_in_type' => $trade_in_type,
'qty' => $qty, 'qty' => $qty,
'title' => $this->getTitle($batt_size, $trade_in_type), 'title' => $this->getTitle($batt_size, $trade_in_type),
'price' => $price, 'price' => $price,
@ -67,6 +72,42 @@ class TradeIn implements InvoiceRuleInterface
public function validateInvoiceItems($criteria, $invoice_items) public function validateInvoiceItems($criteria, $invoice_items)
{ {
// check service type. Only battery sales and battery warranty should have invoice items.
$stype = $criteria->getServiceType();
if ($stype != ServiceType::BATTERY_REPLACEMENT_NEW)
return null;
// return error if there's a problem, false otherwise
if (!empty($invoice_items))
{
// check if this is a valid battery
foreach ($invoice_items as $item)
{
if (isset($item['battery_size']))
{
$battery_size = $this->em->getRepository(BatterySize::class)->find($item['battery_size']);
if (empty($battery_size))
{
$error = 'Invalid battery size specified.';
return $error;
}
// quantity
$qty = $item['quantity'];
if ($qty < 1)
continue;
// check if trade in is set and if trade in type if valid
if (!empty($item['trade_in']) && TradeInType::validate($item['trade_in']))
{
$trade_in = $item['trade_in'];
$criteria->addTradeInEntry($battery_size, $trade_in, $qty);
}
}
}
}
return null; return null;
} }

View file

@ -55,7 +55,7 @@ class InvoiceManager implements InvoiceGeneratorInterface
new InvoiceRule\Fuel($this->em, $this->pt_manager), new InvoiceRule\Fuel($this->em, $this->pt_manager),
new InvoiceRule\TireRepair($this->em, $this->pt_manager), new InvoiceRule\TireRepair($this->em, $this->pt_manager),
new InvoiceRule\DiscountType($this->em), new InvoiceRule\DiscountType($this->em),
new InvoiceRule\TradeIn(), new InvoiceRule\TradeIn($this->em),
new InvoiceRule\Tax($this->em, $this->pt_manager), new InvoiceRule\Tax($this->em, $this->pt_manager),
]; ];
} }
@ -166,6 +166,7 @@ class InvoiceManager implements InvoiceGeneratorInterface
// (3) generateInvoiceCriteria // (3) generateInvoiceCriteria
// (4) RiderAPIHandler's changeService // (4) RiderAPIHandler's changeService
// (5) TAPI's JobOrderController // (5) TAPI's JobOrderController
// (6) CAPI's RiderAppController
public function generateInvoice($criteria) public function generateInvoice($criteria)
{ {
// no need to validate since generateDraftInvoice was called before this was called // no need to validate since generateDraftInvoice was called before this was called
@ -214,17 +215,27 @@ class InvoiceManager implements InvoiceGeneratorInterface
$price = $item['price']; $price = $item['price'];
$battery = null; $battery = null;
$battery_size = null;
$trade_in_type = '';
if (isset($item['battery'])) if (isset($item['battery']))
$battery = $item['battery']; $battery = $item['battery'];
if (isset($item['promo'])) if (isset($item['promo']))
$promo = $item['promo']; $promo = $item['promo'];
if (isset($item['battery_size']))
$battery_size = $item['battery_size'];
if (isset($item['trade_in_type']))
$trade_in_type = $item['trade_in_type'];
$invoice_items[] = [ $invoice_items[] = [
'title' => $title, 'title' => $title,
'quantity' => $quantity, 'quantity' => $quantity,
'price' => $price, 'price' => $price,
'battery' => $battery, 'battery' => $battery,
'battery_size' => $battery_size,
'trade_in_type' => $trade_in_type,
]; ];
} }
} }
@ -273,11 +284,15 @@ class InvoiceManager implements InvoiceGeneratorInterface
$invoice_item->setInvoice($invoice) $invoice_item->setInvoice($invoice)
->setTitle($item['title']) ->setTitle($item['title'])
->setQuantity($item['quantity']) ->setQuantity($item['quantity'])
->setPrice((float)$item['price']); ->setPrice((float)$item['price'])
->setTradeInType($item['trade_in_type']);
if ($item['battery'] != null) if ($item['battery'] != null)
$invoice_item->setBattery($item['battery']); $invoice_item->setBattery($item['battery']);
if ($item['battery_size'] != null)
$invoice_item->setBatterySize($item['battery_size']);
$invoice->addItem($invoice_item); $invoice->addItem($invoice_item);
} }

View file

@ -30,6 +30,7 @@ use App\Entity\EmergencyType;
use App\Entity\OwnershipType; use App\Entity\OwnershipType;
use App\Entity\CustomerLocation; use App\Entity\CustomerLocation;
use App\Entity\Battery; use App\Entity\Battery;
use App\Entity\BatterySize;
use App\Ramcar\ServiceType; use App\Ramcar\ServiceType;
use App\Ramcar\TradeInType; use App\Ramcar\TradeInType;
@ -2312,6 +2313,13 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface
$em = $this->em; $em = $this->em;
$jo = $em->getRepository(JobOrder::class)->find($id); $jo = $em->getRepository(JobOrder::class)->find($id);
// get the job order's invoice items
$invoice = $jo->getInvoice();
$invoice_items = [];
if ($invoice != null)
$invoice_items = $invoice->getItems();
$params['invoice_items'] = $invoice_items;
$params['obj'] = $jo; $params['obj'] = $jo;
$params['mode'] = 'open_edit'; $params['mode'] = 'open_edit';
$params['cvid'] = $jo->getCustomerVehicle()->getID(); $params['cvid'] = $jo->getCustomerVehicle()->getID();
@ -3631,15 +3639,15 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface
$params['trade_in_bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll(); $params['trade_in_bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll();
$params['promos'] = $em->getRepository(Promo::class)->findAll(); $params['promos'] = $em->getRepository(Promo::class)->findAll();
// list of batteries for trade-in // list of battery sizes for trade-in
$ti_batteries = $em->getRepository(Battery::class)->findAll(); $ti_batt_sizes = $em->getRepository(BatterySize::class)->findAll();
$trade_in_batteries = []; $trade_in_batt_sizes = [];
foreach ($ti_batteries as $ti_battery) foreach ($ti_batt_sizes as $ti_batt_size)
{ {
$battery_name = $ti_battery->getModel()->getName() . ' ' . $ti_battery->getSize()->getName(); $batt_size_name = $ti_batt_size->getName();
$trade_in_batteries[$ti_battery->getID()] = $battery_name; $trade_in_batt_sizes[$ti_batt_size->getID()] = $batt_size_name;
} }
$params['trade_in_batteries'] = $trade_in_batteries; $params['trade_in_batt_sizes'] = $trade_in_batt_sizes;
// list of emergency types // list of emergency types
$e_types = $em->getRepository(EmergencyType::class)->findBy([], ['name' => 'ASC']); $e_types = $em->getRepository(EmergencyType::class)->findBy([], ['name' => 'ASC']);

View file

@ -8,11 +8,11 @@
</select> </select>
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<label for="invoice-trade-in-battery">Battery For Trade In</label> <label for="invoice-trade-in-battery-size">Battery Size For Trade In</label>
<select class="form-control m-input" id="invoice-trade-in-battery" data-value=""> <select class="form-control m-input" id="invoice-trade-in-battery-size" data-value="">
<option value=""></option> <option value=""></option>
{% for id, battery_name in trade_in_batteries %} {% for id, batt_size_name in trade_in_batt_sizes %}
<option value="{{ id }}">{{ battery_name }}</option> <option value="{{ id }}">{{ batt_size_name }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>

View file

@ -1,13 +1,13 @@
// add trade in battery to invoice // add trade in battery to invoice
$('#btn-add-trade-in-to-invoice').click(function() { $('#btn-add-trade-in-to-invoice').click(function() {
var bmfg = $("#invoice-trade-in-bmfg").val(); var bmfg = $("#invoice-trade-in-bmfg").val();
var battery = $("#invoice-trade-in-battery").val(); var battery_size = $("#invoice-trade-in-battery-size").val();
var tradeIn = $("#invoice-trade-in-type").val(); var tradeIn = $("#invoice-trade-in-type").val();
var qty = $("#invoice-trade-in-quantity").val(); var qty = $("#invoice-trade-in-quantity").val();
// add to invoice array // add to invoice array
invoiceItems.push({ invoiceItems.push({
battery: battery, battery_size: battery_size,
quantity: qty, quantity: qty,
trade_in: tradeIn, trade_in: tradeIn,
}); });

View file

@ -630,20 +630,24 @@
</div> </div>
</div> </div>
<div class="form-group m-form__group row"> <div class="form-group m-form__group row">
<div class="col-lg-6"> <div class="col-lg-6">
<label>Discount Type</label> <label>Discount Type</label>
{% if ftags.invoice_edit %} {% if ftags.invoice_edit %}
<select class="form-control m-input" id="invoice-promo" name="invoice_promo"> <select class="form-control m-input" id="invoice-promo" name="invoice_promo">
<option value="">None</option> <option value="">None</option>
{% for promo in promos %} {% for promo in promos %}
<option value="{{ promo.getID() }}">{{ promo.getName() ~ ' (' ~ promo.getDiscountRate * 100 ~ '% applied to ' ~ discount_apply[promo.getDiscountApply] ~ ')' }}</option> {% if obj.getInvoice and obj.getInvoice.getPromo %}
{% endfor %} <option value="{{ promo.getID() }}" {{ obj.getInvoice.getPromo.getID == promo.getID ? ' selected'}}>{{ promo.getName() ~ ' (' ~ promo.getDiscountRate * 100 ~ '% applied to ' ~ discount_apply[promo.getDiscountApply] ~ ')' }}</option>
</select> {% else %}
<div class="form-control-feedback hide" data-field="invoice_promo"></div> <option value="{{ promo.getID() }}">{{ promo.getName() ~ ' (' ~ promo.getDiscountRate * 100 ~ '% applied to ' ~ discount_apply[promo.getDiscountApply] ~ ')' }}</option>
{% else %} {% endif %}
<input type="text" id="invoice-promo" class="form-control m-input" value="{{ obj.getInvoice.getPromo.getName|default('None') }}" disabled> {% endfor %}
{% endif %} </select>
</div> <div class="form-control-feedback hide" data-field="invoice_promo"></div>
{% else %}
<input type="text" id="invoice-promo" class="form-control m-input" value="{{ obj.getInvoice.getPromo.getName|default('None') }}" disabled>
{% endif %}
</div>
<div class="col-lg-3"> <div class="col-lg-3">
<label>Promo Discount</label> <label>Promo Discount</label>
<input type="text" id="invoice-promo-discount" class="form-control m-input text-right" value="{{ obj.getInvoice ? obj.getInvoice.getDiscount|number_format(2) : '0.00' }}" disabled> <input type="text" id="invoice-promo-discount" class="form-control m-input text-right" value="{{ obj.getInvoice ? obj.getInvoice.getDiscount|number_format(2) : '0.00' }}" disabled>
@ -1194,6 +1198,8 @@
<script src="/assets/vendors/custom/gmaps/gmaps.js" type="text/javascript"></script> <script src="/assets/vendors/custom/gmaps/gmaps.js" type="text/javascript"></script>
<script> <script>
var invoiceItems = [];
// location search autocomplete // location search autocomplete
var input = document.getElementById('m_gmap_address'); var input = document.getElementById('m_gmap_address');
@ -1218,6 +1224,7 @@ autocomplete.addListener('place_changed', function() {
$(function() { $(function() {
var form_in_process = false; var form_in_process = false;
var invoiceItems = [];
// openstreet maps stuff // openstreet maps stuff
// TODO: move this to a service // TODO: move this to a service
@ -1228,6 +1235,36 @@ $(function() {
var markerLayerGroup = L.layerGroup().addTo(osm_map); var markerLayerGroup = L.layerGroup().addTo(osm_map);
function populateInvoiceItems()
{
{% if invoice_items is defined %}
{% for item in invoice_items %}
var qty = {{ item.getQuantity }};
{% if item.getBattery is not null %}
var battery_id = {{ item.getBattery.getID }};
invoiceItems.push({
battery: battery_id,
quantity: qty,
trade_in: '',
});
{% else %}
{% if item.getBatterySize is not null %}
var battery_size = {{ item.getBatterySize.GetID }};
var trade_in_type = '{{ item.getTradeInType }}';
// add to invoice array
invoiceItems.push({
battery_size: battery_size,
quantity: qty,
trade_in: trade_in_type,
});
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
}
function selectPoint(lat, lng) function selectPoint(lat, lng)
{ {
// check if point is in coverage area // check if point is in coverage area
@ -1266,7 +1303,7 @@ $(function() {
$('#map_lat').val(lat); $('#map_lat').val(lat);
$('#map_lng').val(lng); $('#map_lng').val(lng);
// regenerate invoice // regenerate invoice
generateInvoice(); generateInvoice();
} }
@ -1312,6 +1349,11 @@ $(function() {
// OSM code // OSM code
var lat = {{ obj.getCoordinates.getLatitude }}; var lat = {{ obj.getCoordinates.getLatitude }};
var lng = {{ obj.getCoordinates.getLongitude }}; var lng = {{ obj.getCoordinates.getLongitude }};
var promo = $("#invoice-promo").val();
populateInvoiceItems();
selectPoint(lat, lng); selectPoint(lat, lng);
// remove placeholder text // remove placeholder text
@ -1695,8 +1737,6 @@ $(function() {
placeholder: "" placeholder: ""
}); });
var invoiceItems = [];
{% include 'invoice/trade_in.js.twig' %} {% include 'invoice/trade_in.js.twig' %}
// add to invoice // add to invoice