diff --git a/config/routes/job_order.yaml b/config/routes/job_order.yaml index 3d8cb603..b1ac7872 100644 --- a/config/routes/job_order.yaml +++ b/config/routes/job_order.yaml @@ -8,11 +8,6 @@ jo_in_submit: controller: App\Controller\JobOrderController::incomingSubmit methods: [POST] -jo_in_invoice: - path: /job-order/generate-invoice - controller: App\Controller\JobOrderController::generateInvoice - methods: [POST] - jo_proc: path: /job-order/processing controller: App\Controller\JobOrderController::listRows @@ -59,4 +54,9 @@ jo_assign_form: jo_assign_submit: path: /job-order/assigning/{id} controller: App\Controller\JobOrderController::assigningSubmit + methods: [POST] + +jo_gen_invoice: + path: /job-order/generate-invoice + controller: App\Controller\JobOrderController::generateInvoice methods: [POST] \ No newline at end of file diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index 0d3e1a57..bab6eacb 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -7,6 +7,7 @@ use App\Ramcar\ServiceType; use App\Ramcar\JOStatus; use App\Ramcar\WarrantyClass; use App\Ramcar\DiscountApply; +use App\Ramcar\TradeInType; use App\Ramcar\InvoiceCriteria; use App\Entity\JobOrder; use App\Entity\BatteryManufacturer; @@ -15,6 +16,7 @@ use App\Entity\CustomerVehicle; use App\Entity\Outlet; use App\Entity\Promo; use App\Entity\Rider; +use App\Entity\Battery; use App\Service\InvoiceCreator; use App\Service\MapTools; @@ -50,6 +52,7 @@ class JobOrderController extends BaseController $params['warranty_classes'] = WarrantyClass::getCollection(); $params['statuses'] = JOStatus::getCollection(); $params['discount_apply'] = DiscountApply::getCollection(); + $params['trade_in_types'] = TradeInType::getCollection(); // response return $this->render('job-order/form.html.twig', $params); @@ -287,6 +290,7 @@ class JobOrderController extends BaseController $params['statuses'] = JOStatus::getCollection(); $params['promos'] = $em->getRepository(Promo::class)->findAll(); $params['discount_apply'] = DiscountApply::getCollection(); + $params['trade_in_types'] = TradeInType::getCollection(); // get closest outlets $outlets = $map_tools->getClosestOutlets($obj->getCoordinates(), 10, date("H:i:s")); @@ -426,6 +430,7 @@ class JobOrderController extends BaseController $params['statuses'] = JOStatus::getCollection(); $params['promos'] = $em->getRepository(Promo::class)->findAll(); $params['discount_apply'] = DiscountApply::getCollection(); + $params['trade_in_types'] = TradeInType::getCollection(); // get closest outlets $outlets = $map_tools->getClosestOutlets($obj->getCoordinates(), 10, date("H:i:s")); @@ -572,9 +577,85 @@ class JobOrderController extends BaseController public function generateInvoice(Request $req, InvoiceCreator $ic) { - // create new invoice object - $invoice = new InvoiceCriteria(); + $error = false; - //$invoice-> + $items = $req->request->get('items'); + $promo_id = $req->request->get('promo'); + + $em = $this->getDoctrine()->getManager(); + + // instantiate invoice criteria + $criteria = new InvoiceCriteria(); + + if (!empty($promo_id)) + { + // check if this is a valid promo + $promo = $em->getRepository(Promo::class)->find($promo_id); + + if (empty($promo)) + $error = 'Invalid promo specified.'; + else + $criteria->addPromo($promo); + } + + if (!$error) + { + foreach ($items as $item) + { + // check if this is a valid battery + $battery = $em->getRepository(Battery::class)->find($item['battery']); + + if (empty($battery)) + { + $error = 'Invalid battery specified.'; + break; + } + + // add to criteria + $criteria->addBattery($battery); + + // if this is a trade in, add trade in + if (!empty($item['trade_in']) && TradeInType::validate($item['trade_in'])) + $criteria->addTradeIn($item['trade_in'] == 'motolite' ? true : false); + } + } + + if ($error) + { + // something happened + return $this->json([ + 'success' => false, + 'error' => $error + ], 422); + } + + // generate the invoice + $iobj = $ic->processCriteria($criteria); + + // use invoice object values in a json friendly array + $invoice = [ + 'discount' => number_format($iobj->getDiscount(), 2), + 'trade_in' => number_format($iobj->getTradeIn(), 2), // TODO: computations not done yet for this on invoice creator + 'price' => number_format($iobj->getVATExclusivePrice(), 2), // TODO: computations not done yet for this on invoice creator + 'vat' => number_format($iobj->getVAT(), 2), + 'total_price' => number_format($iobj->getTotalPrice(), 2), + 'items' => [] + ]; + + foreach ($iobj->getItems() as $item) + { + $invoice['items'][] = [ + 'title' => $item->getTitle(), + 'quantity' => $item->getQuantity(), // TODO: quantities are always 1, hardcoded into InvoiceCreator. no way of accepting quantities on InvoiceCriteria + 'unit_price' => number_format($item->getPrice(), 2), + 'amount' => number_format($item->getPrice() * $item->getQuantity(), 2) // TODO: should this calculation should be a saved value on InvoiceItem instead? + ]; + } + + // return + return $this->json([ + 'success' => true, + 'invoice' => $invoice + ]); } } diff --git a/src/Entity/Invoice.php b/src/Entity/Invoice.php index e28a8f77..e92e9abf 100644 --- a/src/Entity/Invoice.php +++ b/src/Entity/Invoice.php @@ -1,6 +1,6 @@ discount; } + public function setTradeIn($trade_in) + { + $this->trade_in = $trade_in; + return $this; + } + + public function getTradeIn() + { + return $this->trade_in; + } + public function setVAT($vat) { $this->vat = $vat; @@ -182,6 +205,17 @@ class Invoice return $this->vat; } + public function setVATExclusivePrice($price) + { + $this->vat_exclusive_price = $price; + return $this; + } + + public function getVATExclusivePrice() + { + return $this->vat_exclusive_price; + } + public function setTotalPrice($price) { $this->total_price = $price; diff --git a/src/Entity/InvoiceItem.php b/src/Entity/InvoiceItem.php index 31fb887e..1ea66765 100644 --- a/src/Entity/InvoiceItem.php +++ b/src/Entity/InvoiceItem.php @@ -1,6 +1,6 @@ batteries; } - public function addPromo($promo) + public function addPromo(Promo $promo) { $this->promos[] = $promo; return $this; diff --git a/src/Ramcar/TradeInType.php b/src/Ramcar/TradeInType.php new file mode 100644 index 00000000..eeb559fa --- /dev/null +++ b/src/Ramcar/TradeInType.php @@ -0,0 +1,14 @@ + 'Motolite', + 'other' => 'Other', + ]; +} diff --git a/src/Service/InvoiceCreator.php b/src/Service/InvoiceCreator.php index bf5fdd00..b54635fd 100644 --- a/src/Service/InvoiceCreator.php +++ b/src/Service/InvoiceCreator.php @@ -35,9 +35,9 @@ class InvoiceCreator public function getTradeInRate($trade_in) { if ($trade_in == 'motolite') - return TIRATE_MOTOLITE; + return self::TIRATE_MOTOLITE; - return TIRATE_OTHER; + return self::TIRATE_OTHER; } public function processCriteria(InvoiceCriteria $criteria) @@ -71,9 +71,11 @@ class InvoiceCreator // add item $item = new InvoiceItem(); $item->setInvoice($invoice) - ->setTitle($batt->getModel() . ' ' . $batt->getSize()) + ->setTitle($batt->getModel()->getName() . ' ' . $batt->getSize()->getName()) ->setQuantity(1) ->setPrice($sell_price); + + $invoice->addItem($item); } // get trade-ins @@ -92,6 +94,8 @@ class InvoiceCreator ->setTitle('Trade-in battery') ->setQuantity(1) ->setPrice($ti_rate); + + $invoice->addItem($item); } // TODO: check if any promo is applied @@ -104,7 +108,7 @@ class InvoiceCreator // dump - Debug::dump($invoice, 1); + //Debug::dump($invoice, 1); return $invoice; } diff --git a/templates/job-order/form.html.twig b/templates/job-order/form.html.twig index 7c9e7166..32c94240 100644 --- a/templates/job-order/form.html.twig +++ b/templates/job-order/form.html.twig @@ -303,16 +303,14 @@ Item Quantity - Warranty Class - Warranty - Date of Purchase - Warranty Until - + Unit Price + Amount + - + No items to display. @@ -321,23 +319,35 @@
-
+
+
-
+
+
- + +
-
+
+ + +
+
+
@@ -853,8 +863,7 @@ $(function() { var unitPrice = 1000; $.each(response.data, function(index, battery) { - console.log(battery); - html += ''; + html += ''; }); field.html(html).prop('disabled', false); @@ -862,7 +871,7 @@ $(function() { field.html('').prop('disabled', true); } }); - }); + }).change(); // mask quantity field $("#invoice-quantity").inputmask("mask", { @@ -871,21 +880,15 @@ $(function() { }); var invoiceItems = []; - var invoiceDiscount = 0; - var invoiceTax = 0; - var invoiceTotal = 0; // add to invoice $("#btn-add-to-invoice").click(function() { - var bmfg = $("#invoice-bmfg"); - var battery = $("#invoice-battery"); - var qty = $("#invoice-quantity"); - var tbody = $("#invoice-table tbody"); - var unitPrice = parseFloat(battery.find('option:selected').data('unit-price')); - var warrPrivate = parseInt(battery.find('option:selected').data('warr-private')); - var warrCommercial = parseInt(battery.find('option:selected').data('warr-commercial')); + var bmfg = $("#invoice-bmfg").val(); + var battery = $("#invoice-battery").val(); + var tradeIn = $("#invoice-trade-in-type").val(); + var qty = $("#invoice-quantity").val(); - if (!bmfg.val() || !battery.val() || !qty.val()) { + if (!bmfg || !battery || !qty) { swal({ title: 'Whoops!', text: 'Please fill in all the invoice fields.', @@ -895,7 +898,7 @@ $(function() { return false; } - if (isNaN(qty.val())) { + if (isNaN(qty)) { swal({ title: 'Whoops!', text: 'Invalid quantity specified.', @@ -905,76 +908,71 @@ $(function() { return false; } - var qtyVal = parseInt(qty.val()); - var subtotal = unitPrice * qtyVal; - - // add to invoice array for form submission + // add to invoice array invoiceItems.push({ - 'battery_manufacturer': bmfg.val(), - 'battery': battery.val(), - 'battery_price': unitPrice, - 'quantity': qtyVal, - 'subtotal': subtotal + 'battery': battery, + 'quantity': qty, + 'trade_in': tradeIn, }); - var warrantyClasses = { - {% for key, class in warranty_classes %} - "{{ key }}": "{{ class }}", - {% endfor %} - }; - - var warrantyClass = $("#warranty-class").val(); - var warrantyLength = warrantyClass == warrantyClasses["private"] ? warrPrivate : warrCommercial; - - // build row html - // TODO: Fill with actual data for the rest of the columns - var html = '' + - '' + battery.find('option:selected').text() + '' + - '' + qty.val() + '' + - '' + warrantyClasses[warrantyClass] + '' + - '' + warrantyLength + ' month' + (warrantyLength > 1 ? 's' : '') + '' + - '' + // TODO: Date of Purchase, blank for now - '' + // TODO: Warranty until, blank for now - '' + - ''; - - // save to invoice table - tbody.find('.placeholder-row').addClass('hide'); - tbody.append(html); - - // add to invoice total and recompute - invoiceTotal += subtotal; - updateInvoiceFigures(); + // regenerate the invoice + generateInvoice(); }); - function updateInvoiceFigures() { - var amountField = $("#invoice-price"); - var taxField = $("#invoice-vat"); - var discountField = $("#invoice-discount"); - var finalAmountField = $("#invoice-total-amount"); - var finalAmount = invoiceTotal + invoiceTax - invoiceDiscount; + function generateInvoice() { + var promo = $("#invoice-promo").val(); + var table = $("#invoice-table tbody"); - amountField.val(parseFloat(Math.round(invoiceTotal * 100) / 100).toFixed(2)); - taxField.val(parseFloat(Math.round(invoiceTax * 100) / 100).toFixed(2)); - discountField.val(parseFloat(Math.round(invoiceDiscount * 100) / 100).toFixed(2)); - finalAmountField.val(parseFloat(Math.round(finalAmount * 100) / 100).toFixed(2)); + // generate invoice values + $.ajax({ + method: "POST", + url: "{{ url('jo_gen_invoice') }}", + data: { + 'items': invoiceItems, + 'promo': promo + } + }).done(function(response) { + var invoice = response.invoice; + + // populate totals + $("#invoice-promo-discount").val(invoice.discount); + $("#invoice-price").val(invoice.price); + $("#invoice-trade-in").val(invoice.trade_in); + $("#invoice-vat").val(invoice.vat); + $("#invoice-total-amount").val(invoice.total_price); + + // populate rows + var html = ''; + + $.each(invoice.items, function(key, item) { + html += '' + + '' + item.title + '' + + '' + item.quantity + '' + + '' + item.unit_price + '' + + '' + item.amount + '' + + /* + '' + + */ + ''; + }); + + // save to invoice table + table.find('.placeholder-row').addClass('hide'); + table.html(html); + }); } // remove from invoice table + // TODO: figure out a way to delete rows, and should deleting trade ins be allowed since they count as items on the table? + /* $(document).on("click", ".btn-invoice-delete", function() { var tbody = $("#invoice-table tbody"); var row = $(this).closest('tr'); var qty = row.find('.col-quantity').html(); var unitPrice = row.find('.col-unit-price').html(); - // set defaults if for whatever reason they are not valid numbers - if (isNaN(qty)) { - qty = 1; - } - - if (isNaN(unitPrice)) { - unitPrice = 0; - } + // recreate the local invoice items object + var newItems = []; // subtract from totals var subtotal = parseFloat(unitPrice) * parseInt(qty); @@ -991,6 +989,7 @@ $(function() { tbody.find('.placeholder-row').removeClass('hide'); } }); + */ {% if mode == 'update-processing' %} var selectedOutlet = '{{ obj.getOutlet ? obj.getOutlet.getID : "" }}';