Merge branch 'master' of gitlab.com:jankstudio/resq
This commit is contained in:
commit
fac3c409c4
8 changed files with 268 additions and 105 deletions
|
|
@ -54,4 +54,9 @@ jo_assign_form:
|
||||||
jo_assign_submit:
|
jo_assign_submit:
|
||||||
path: /job-order/assigning/{id}
|
path: /job-order/assigning/{id}
|
||||||
controller: App\Controller\JobOrderController::assigningSubmit
|
controller: App\Controller\JobOrderController::assigningSubmit
|
||||||
|
methods: [POST]
|
||||||
|
|
||||||
|
jo_gen_invoice:
|
||||||
|
path: /job-order/generate-invoice
|
||||||
|
controller: App\Controller\JobOrderController::generateInvoice
|
||||||
methods: [POST]
|
methods: [POST]
|
||||||
|
|
@ -6,13 +6,19 @@ use App\Ramcar\BaseController;
|
||||||
use App\Ramcar\ServiceType;
|
use App\Ramcar\ServiceType;
|
||||||
use App\Ramcar\JOStatus;
|
use App\Ramcar\JOStatus;
|
||||||
use App\Ramcar\WarrantyClass;
|
use App\Ramcar\WarrantyClass;
|
||||||
|
use App\Ramcar\DiscountApply;
|
||||||
|
use App\Ramcar\TradeInType;
|
||||||
|
use App\Ramcar\InvoiceCriteria;
|
||||||
use App\Entity\JobOrder;
|
use App\Entity\JobOrder;
|
||||||
use App\Entity\BatteryManufacturer;
|
use App\Entity\BatteryManufacturer;
|
||||||
use App\Entity\Customer;
|
use App\Entity\Customer;
|
||||||
use App\Entity\CustomerVehicle;
|
use App\Entity\CustomerVehicle;
|
||||||
use App\Entity\Outlet;
|
use App\Entity\Outlet;
|
||||||
|
use App\Entity\Promo;
|
||||||
use App\Entity\Rider;
|
use App\Entity\Rider;
|
||||||
|
use App\Entity\Battery;
|
||||||
|
|
||||||
|
use App\Service\InvoiceCreator;
|
||||||
use App\Service\MapTools;
|
use App\Service\MapTools;
|
||||||
|
|
||||||
use Doctrine\ORM\Query;
|
use Doctrine\ORM\Query;
|
||||||
|
|
@ -41,11 +47,12 @@ class JobOrderController extends BaseController
|
||||||
// get parent associations
|
// get parent associations
|
||||||
$params['bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll();
|
$params['bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll();
|
||||||
$params['customers'] = $em->getRepository(Customer::class)->findAll();
|
$params['customers'] = $em->getRepository(Customer::class)->findAll();
|
||||||
$params['outlet'] = $em->getRepository(Outlet::class)->findAll();
|
$params['promos'] = $em->getRepository(Promo::class)->findAll();
|
||||||
$params['rider'] = $em->getRepository(Rider::class)->findAll();
|
|
||||||
$params['service_types'] = ServiceType::getCollection();
|
$params['service_types'] = ServiceType::getCollection();
|
||||||
$params['warranty_classes'] = WarrantyClass::getCollection();
|
$params['warranty_classes'] = WarrantyClass::getCollection();
|
||||||
$params['statuses'] = JOStatus::getCollection();
|
$params['statuses'] = JOStatus::getCollection();
|
||||||
|
$params['discount_apply'] = DiscountApply::getCollection();
|
||||||
|
$params['trade_in_types'] = TradeInType::getCollection();
|
||||||
|
|
||||||
// response
|
// response
|
||||||
return $this->render('job-order/form.html.twig', $params);
|
return $this->render('job-order/form.html.twig', $params);
|
||||||
|
|
@ -281,6 +288,9 @@ class JobOrderController extends BaseController
|
||||||
$params['service_types'] = ServiceType::getCollection();
|
$params['service_types'] = ServiceType::getCollection();
|
||||||
$params['warranty_classes'] = WarrantyClass::getCollection();
|
$params['warranty_classes'] = WarrantyClass::getCollection();
|
||||||
$params['statuses'] = JOStatus::getCollection();
|
$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
|
// get closest outlets
|
||||||
$outlets = $map_tools->getClosestOutlets($obj->getCoordinates(), 10, date("H:i:s"));
|
$outlets = $map_tools->getClosestOutlets($obj->getCoordinates(), 10, date("H:i:s"));
|
||||||
|
|
@ -418,6 +428,9 @@ class JobOrderController extends BaseController
|
||||||
$params['service_types'] = ServiceType::getCollection();
|
$params['service_types'] = ServiceType::getCollection();
|
||||||
$params['warranty_classes'] = WarrantyClass::getCollection();
|
$params['warranty_classes'] = WarrantyClass::getCollection();
|
||||||
$params['statuses'] = JOStatus::getCollection();
|
$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
|
// get closest outlets
|
||||||
$outlets = $map_tools->getClosestOutlets($obj->getCoordinates(), 10, date("H:i:s"));
|
$outlets = $map_tools->getClosestOutlets($obj->getCoordinates(), 10, date("H:i:s"));
|
||||||
|
|
@ -540,7 +553,8 @@ class JobOrderController extends BaseController
|
||||||
// TODO: re-enable search, figure out how to group the orWhere filters into one, so can execute that plus the pending filter
|
// TODO: re-enable search, figure out how to group the orWhere filters into one, so can execute that plus the pending filter
|
||||||
|
|
||||||
// check if datatable filter is present and append to query
|
// check if datatable filter is present and append to query
|
||||||
protected function setQueryFilters($datatable, &$query, $qb, $status) {
|
protected function setQueryFilters($datatable, &$query, $qb, $status)
|
||||||
|
{
|
||||||
$query->where('q.status = :status')
|
$query->where('q.status = :status')
|
||||||
->setParameter('status', $status);
|
->setParameter('status', $status);
|
||||||
|
|
||||||
|
|
@ -560,4 +574,88 @@ class JobOrderController extends BaseController
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generateInvoice(Request $req, InvoiceCreator $ic)
|
||||||
|
{
|
||||||
|
$error = false;
|
||||||
|
|
||||||
|
$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
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Entity;
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
|
@ -64,12 +64,24 @@ class Invoice
|
||||||
*/
|
*/
|
||||||
protected $discount;
|
protected $discount;
|
||||||
|
|
||||||
|
// total trade in (amount)
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="decimal", precision=9, scale=2)
|
||||||
|
*/
|
||||||
|
protected $trade_in;
|
||||||
|
|
||||||
// total vat (amount, not %)
|
// total vat (amount, not %)
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="decimal", precision=9, scale=2)
|
* @ORM\Column(type="decimal", precision=9, scale=2)
|
||||||
*/
|
*/
|
||||||
protected $vat;
|
protected $vat;
|
||||||
|
|
||||||
|
// vat exclusive price (amount)
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="decimal", precision=9, scale=2)
|
||||||
|
*/
|
||||||
|
protected $vat_exclusive_price;
|
||||||
|
|
||||||
// total price (amount)
|
// total price (amount)
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="decimal", precision=9, scale=2)
|
* @ORM\Column(type="decimal", precision=9, scale=2)
|
||||||
|
|
@ -171,6 +183,17 @@ class Invoice
|
||||||
return $this->discount;
|
return $this->discount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setTradeIn($trade_in)
|
||||||
|
{
|
||||||
|
$this->trade_in = $trade_in;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTradeIn()
|
||||||
|
{
|
||||||
|
return $this->trade_in;
|
||||||
|
}
|
||||||
|
|
||||||
public function setVAT($vat)
|
public function setVAT($vat)
|
||||||
{
|
{
|
||||||
$this->vat = $vat;
|
$this->vat = $vat;
|
||||||
|
|
@ -182,6 +205,17 @@ class Invoice
|
||||||
return $this->vat;
|
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)
|
public function setTotalPrice($price)
|
||||||
{
|
{
|
||||||
$this->total_price = $price;
|
$this->total_price = $price;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Entity;
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Ramcar;
|
namespace App\Ramcar;
|
||||||
|
|
||||||
use App\Entity\Battery;
|
use App\Entity\Battery;
|
||||||
|
use App\Entity\Promo;
|
||||||
|
|
||||||
class InvoiceCriteria
|
class InvoiceCriteria
|
||||||
{
|
{
|
||||||
|
|
@ -28,7 +29,7 @@ class InvoiceCriteria
|
||||||
return $this->batteries;
|
return $this->batteries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addPromo($promo)
|
public function addPromo(Promo $promo)
|
||||||
{
|
{
|
||||||
$this->promos[] = $promo;
|
$this->promos[] = $promo;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
||||||
14
src/Ramcar/TradeInType.php
Normal file
14
src/Ramcar/TradeInType.php
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ramcar;
|
||||||
|
|
||||||
|
class TradeInType extends NameValue
|
||||||
|
{
|
||||||
|
const MOTOLITE = 'motolite';
|
||||||
|
const OTHER = 'other';
|
||||||
|
|
||||||
|
const COLLECTION = [
|
||||||
|
'motolite' => 'Motolite',
|
||||||
|
'other' => 'Other',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -35,9 +35,9 @@ class InvoiceCreator
|
||||||
public function getTradeInRate($trade_in)
|
public function getTradeInRate($trade_in)
|
||||||
{
|
{
|
||||||
if ($trade_in == 'motolite')
|
if ($trade_in == 'motolite')
|
||||||
return TIRATE_MOTOLITE;
|
return self::TIRATE_MOTOLITE;
|
||||||
|
|
||||||
return TIRATE_OTHER;
|
return self::TIRATE_OTHER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processCriteria(InvoiceCriteria $criteria)
|
public function processCriteria(InvoiceCriteria $criteria)
|
||||||
|
|
@ -71,9 +71,11 @@ class InvoiceCreator
|
||||||
// add item
|
// add item
|
||||||
$item = new InvoiceItem();
|
$item = new InvoiceItem();
|
||||||
$item->setInvoice($invoice)
|
$item->setInvoice($invoice)
|
||||||
->setTitle($batt->getModel() . ' ' . $batt->getSize())
|
->setTitle($batt->getModel()->getName() . ' ' . $batt->getSize()->getName())
|
||||||
->setQuantity(1)
|
->setQuantity(1)
|
||||||
->setPrice($sell_price);
|
->setPrice($sell_price);
|
||||||
|
|
||||||
|
$invoice->addItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get trade-ins
|
// get trade-ins
|
||||||
|
|
@ -92,6 +94,8 @@ class InvoiceCreator
|
||||||
->setTitle('Trade-in battery')
|
->setTitle('Trade-in battery')
|
||||||
->setQuantity(1)
|
->setQuantity(1)
|
||||||
->setPrice($ti_rate);
|
->setPrice($ti_rate);
|
||||||
|
|
||||||
|
$invoice->addItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check if any promo is applied
|
// TODO: check if any promo is applied
|
||||||
|
|
@ -104,7 +108,7 @@ class InvoiceCreator
|
||||||
|
|
||||||
|
|
||||||
// dump
|
// dump
|
||||||
Debug::dump($invoice, 1);
|
//Debug::dump($invoice, 1);
|
||||||
|
|
||||||
return $invoice;
|
return $invoice;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -263,27 +263,35 @@
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group m-form__group row">
|
<div class="form-group m-form__group row">
|
||||||
<div class="col-lg-2">
|
<div class="col-lg-6">
|
||||||
<label>Price</label>
|
<label>Discount Type</label>
|
||||||
<input type="text" id="invoice-price" class="form-control m-input text-right" value="0.00" disabled>
|
<select class="form-control m-input" id="invoice-promo" name="invoice-promo">
|
||||||
|
<option value="">None</option>
|
||||||
|
{% for promo in promos %}
|
||||||
|
<option value="{{ promo.getID() }}">{{ promo.getName() ~ ' (' ~ promo.getDiscountRate * 100 ~ '% applied to ' ~ discount_apply[promo.getDiscountApply] ~ ')' }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<div class="form-control-feedback hide" data-field="invoice_promo"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-2">
|
<div class="col-lg-3">
|
||||||
<label>VAT</label>
|
|
||||||
<input type="text" id="invoice-vat" class="form-control m-input text-right" value="0.00" disabled>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-2">
|
|
||||||
<label>Promo Discount</label>
|
<label>Promo Discount</label>
|
||||||
<input type="text" id="invoice-promo-discount" class="form-control m-input text-right" value="0.00" disabled>
|
<input type="text" id="invoice-promo-discount" class="form-control m-input text-right" value="0.00" disabled>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-2">
|
<div class="col-lg-3">
|
||||||
<label>Discount Type</label>
|
|
||||||
<input type="text" id="invoice-discount-type" class="form-control m-input" value="PVC" disabled>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-2">
|
|
||||||
<label>Trade In</label>
|
<label>Trade In</label>
|
||||||
<input type="text" id="invoice-trade-in" class="form-control m-input text-right" value="0.00" disabled>
|
<input type="text" id="invoice-trade-in" class="form-control m-input text-right" value="0.00" disabled>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-2">
|
</div>
|
||||||
|
<div class="form-group m-form__group row">
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<label>Price</label>
|
||||||
|
<input type="text" id="invoice-price" class="form-control m-input text-right" value="0.00" disabled>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<label>VAT</label>
|
||||||
|
<input type="text" id="invoice-vat" class="form-control m-input text-right" value="0.00" disabled>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
<label>Total Amount</label>
|
<label>Total Amount</label>
|
||||||
<input type="text" id="invoice-total-amount" class="form-control m-input text-right" value="0.00" disabled>
|
<input type="text" id="invoice-total-amount" class="form-control m-input text-right" value="0.00" disabled>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -295,16 +303,14 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th>Item</th>
|
<th>Item</th>
|
||||||
<th class="text-right">Quantity</th>
|
<th class="text-right">Quantity</th>
|
||||||
<th>Warranty Class</th>
|
<th class="text-right">Unit Price</th>
|
||||||
<th>Warranty</th>
|
<th class="text-right">Amount</th>
|
||||||
<th>Date of Purchase</th>
|
<!--<th style="width: 50px;"></th>-->
|
||||||
<th>Warranty Until</th>
|
|
||||||
<th style="width: 50px;"></th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="placeholder-row">
|
<tr class="placeholder-row">
|
||||||
<td colspan="7">
|
<td colspan="4">
|
||||||
No items to display.
|
No items to display.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -313,23 +319,35 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group m-form__group row">
|
<div class="form-group m-form__group row">
|
||||||
<div class="col-lg-3">
|
<div class="col-lg-2">
|
||||||
|
<label for="invoice-bmfg">Manufacturer</label>
|
||||||
<select class="form-control m-input" id="invoice-bmfg">
|
<select class="form-control m-input" id="invoice-bmfg">
|
||||||
<option value="">Select a battery manufacturer</option>
|
|
||||||
{% for manufacturer in bmfgs %}
|
{% for manufacturer in bmfgs %}
|
||||||
<option value="{{ manufacturer.getID() }}">{{ manufacturer.getName() }}</option>
|
<option value="{{ manufacturer.getID() }}">{{ manufacturer.getName() }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-3">
|
<div class="col-lg-2">
|
||||||
|
<label for="invoice-battery">Battery</label>
|
||||||
<select class="form-control m-input" id="invoice-battery" data-value="" disabled>
|
<select class="form-control m-input" id="invoice-battery" data-value="" disabled>
|
||||||
<option value="">Select a manufacturer first</option>
|
<option value="">Select a manufacturer first</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-2">
|
<div class="col-lg-2">
|
||||||
<input type="text" id="invoice-quantity" class="form-control m-input text-right" placeholder="Quantity">
|
<label for="invoice-trade-in-type">Trade In</label>
|
||||||
|
<select class="form-control m-input" id="invoice-trade-in-type">
|
||||||
|
<option value="">None</option>
|
||||||
|
{% for key, type in trade_in_types %}
|
||||||
|
<option value="{{ key }}">{{ type }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-2">
|
||||||
|
<label for="invoice-quantity">Quantity</label>
|
||||||
|
<input type="text" id="invoice-quantity" class="form-control m-input text-right" value="1">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-2">
|
||||||
|
<div><label> </label></div>
|
||||||
<button type="button" class="btn btn-primary" id="btn-add-to-invoice">Add to Invoice</button>
|
<button type="button" class="btn btn-primary" id="btn-add-to-invoice">Add to Invoice</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -845,8 +863,7 @@ $(function() {
|
||||||
var unitPrice = 1000;
|
var unitPrice = 1000;
|
||||||
|
|
||||||
$.each(response.data, function(index, battery) {
|
$.each(response.data, function(index, battery) {
|
||||||
console.log(battery);
|
html += '<option value="' + battery.id + '" data-index="' + index + '" data-warr-private="' + battery.warr_private + '" data-warr-commercial="' + battery.warr_commercial + '">' + battery.model_name + ' ' + battery.size_name + '</option>';
|
||||||
html += '<option value="' + battery.id + '" data-index="' + index + '" data-unit-price="' + battery.sell_price + '" data-warr-private="' + battery.warr_private + '" data-warr-commercial="' + battery.warr_commercial + '">' + battery.model_name + ' ' + battery.size_name + '</option>';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
field.html(html).prop('disabled', false);
|
field.html(html).prop('disabled', false);
|
||||||
|
|
@ -854,7 +871,7 @@ $(function() {
|
||||||
field.html('<option value="">No batteries found</option>').prop('disabled', true);
|
field.html('<option value="">No batteries found</option>').prop('disabled', true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
}).change();
|
||||||
|
|
||||||
// mask quantity field
|
// mask quantity field
|
||||||
$("#invoice-quantity").inputmask("mask", {
|
$("#invoice-quantity").inputmask("mask", {
|
||||||
|
|
@ -863,21 +880,15 @@ $(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
var invoiceItems = [];
|
var invoiceItems = [];
|
||||||
var invoiceDiscount = 0;
|
|
||||||
var invoiceTax = 0;
|
|
||||||
var invoiceTotal = 0;
|
|
||||||
|
|
||||||
// add to invoice
|
// add to invoice
|
||||||
$("#btn-add-to-invoice").click(function() {
|
$("#btn-add-to-invoice").click(function() {
|
||||||
var bmfg = $("#invoice-bmfg");
|
var bmfg = $("#invoice-bmfg").val();
|
||||||
var battery = $("#invoice-battery");
|
var battery = $("#invoice-battery").val();
|
||||||
var qty = $("#invoice-quantity");
|
var tradeIn = $("#invoice-trade-in-type").val();
|
||||||
var tbody = $("#invoice-table tbody");
|
var qty = $("#invoice-quantity").val();
|
||||||
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'));
|
|
||||||
|
|
||||||
if (!bmfg.val() || !battery.val() || !qty.val()) {
|
if (!bmfg || !battery || !qty) {
|
||||||
swal({
|
swal({
|
||||||
title: 'Whoops!',
|
title: 'Whoops!',
|
||||||
text: 'Please fill in all the invoice fields.',
|
text: 'Please fill in all the invoice fields.',
|
||||||
|
|
@ -887,7 +898,7 @@ $(function() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN(qty.val())) {
|
if (isNaN(qty)) {
|
||||||
swal({
|
swal({
|
||||||
title: 'Whoops!',
|
title: 'Whoops!',
|
||||||
text: 'Invalid quantity specified.',
|
text: 'Invalid quantity specified.',
|
||||||
|
|
@ -897,76 +908,71 @@ $(function() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var qtyVal = parseInt(qty.val());
|
// add to invoice array
|
||||||
var subtotal = unitPrice * qtyVal;
|
|
||||||
|
|
||||||
// add to invoice array for form submission
|
|
||||||
invoiceItems.push({
|
invoiceItems.push({
|
||||||
'battery_manufacturer': bmfg.val(),
|
'battery': battery,
|
||||||
'battery': battery.val(),
|
'quantity': qty,
|
||||||
'battery_price': unitPrice,
|
'trade_in': tradeIn,
|
||||||
'quantity': qtyVal,
|
|
||||||
'subtotal': subtotal
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var warrantyClasses = {
|
// regenerate the invoice
|
||||||
{% for key, class in warranty_classes %}
|
generateInvoice();
|
||||||
"{{ 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 = '<tr>' +
|
|
||||||
'<td>' + battery.find('option:selected').text() + '</td>' +
|
|
||||||
'<td class="text-right col-quantity">' + qty.val() + '</td>' +
|
|
||||||
'<td>' + warrantyClasses[warrantyClass] + '</td>' +
|
|
||||||
'<td>' + warrantyLength + ' month' + (warrantyLength > 1 ? 's' : '') + '</td>' +
|
|
||||||
'<td></td>' + // TODO: Date of Purchase, blank for now
|
|
||||||
'<td></td>' + // TODO: Warranty until, blank for now
|
|
||||||
'<td><button type="button" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill btn-invoice-delete" title="Delete"><i class="la la-trash"></i></button></td>' +
|
|
||||||
'</tr>';
|
|
||||||
|
|
||||||
// save to invoice table
|
|
||||||
tbody.find('.placeholder-row').addClass('hide');
|
|
||||||
tbody.append(html);
|
|
||||||
|
|
||||||
// add to invoice total and recompute
|
|
||||||
invoiceTotal += subtotal;
|
|
||||||
updateInvoiceFigures();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateInvoiceFigures() {
|
function generateInvoice() {
|
||||||
var amountField = $("#invoice-price");
|
var promo = $("#invoice-promo").val();
|
||||||
var taxField = $("#invoice-vat");
|
var table = $("#invoice-table tbody");
|
||||||
var discountField = $("#invoice-discount");
|
|
||||||
var finalAmountField = $("#invoice-total-amount");
|
|
||||||
var finalAmount = invoiceTotal + invoiceTax - invoiceDiscount;
|
|
||||||
|
|
||||||
amountField.val(parseFloat(Math.round(invoiceTotal * 100) / 100).toFixed(2));
|
// generate invoice values
|
||||||
taxField.val(parseFloat(Math.round(invoiceTax * 100) / 100).toFixed(2));
|
$.ajax({
|
||||||
discountField.val(parseFloat(Math.round(invoiceDiscount * 100) / 100).toFixed(2));
|
method: "POST",
|
||||||
finalAmountField.val(parseFloat(Math.round(finalAmount * 100) / 100).toFixed(2));
|
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 += '<tr data-key="' + key + '">' +
|
||||||
|
'<td>' + item.title + '</td>' +
|
||||||
|
'<td class="text-right">' + item.quantity + '</td>' +
|
||||||
|
'<td class="text-right">' + item.unit_price + '</td>' +
|
||||||
|
'<td class="text-right">' + item.amount + '</td>' +
|
||||||
|
/*
|
||||||
|
'<td><button type="button" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill btn-invoice-delete" title="Delete"><i class="la la-trash"></i></button></td>' +
|
||||||
|
*/
|
||||||
|
'</tr>';
|
||||||
|
});
|
||||||
|
|
||||||
|
// save to invoice table
|
||||||
|
table.find('.placeholder-row').addClass('hide');
|
||||||
|
table.html(html);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove from invoice table
|
// 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() {
|
$(document).on("click", ".btn-invoice-delete", function() {
|
||||||
var tbody = $("#invoice-table tbody");
|
var tbody = $("#invoice-table tbody");
|
||||||
var row = $(this).closest('tr');
|
var row = $(this).closest('tr');
|
||||||
var qty = row.find('.col-quantity').html();
|
var qty = row.find('.col-quantity').html();
|
||||||
var unitPrice = row.find('.col-unit-price').html();
|
var unitPrice = row.find('.col-unit-price').html();
|
||||||
|
|
||||||
// set defaults if for whatever reason they are not valid numbers
|
// recreate the local invoice items object
|
||||||
if (isNaN(qty)) {
|
var newItems = [];
|
||||||
qty = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNaN(unitPrice)) {
|
|
||||||
unitPrice = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// subtract from totals
|
// subtract from totals
|
||||||
var subtotal = parseFloat(unitPrice) * parseInt(qty);
|
var subtotal = parseFloat(unitPrice) * parseInt(qty);
|
||||||
|
|
@ -983,6 +989,7 @@ $(function() {
|
||||||
tbody.find('.placeholder-row').removeClass('hide');
|
tbody.find('.placeholder-row').removeClass('hide');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
{% if mode == 'update-processing' %}
|
{% if mode == 'update-processing' %}
|
||||||
var selectedOutlet = '{{ obj.getOutlet ? obj.getOutlet.getID : "" }}';
|
var selectedOutlet = '{{ obj.getOutlet ? obj.getOutlet.getID : "" }}';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue