Orders CRUD
Last updated
Last updated
This form is complicated. It has a repeater field for adding product lines. And it also has javascript to calculate Totals.
All other things (Adding controller, routes, etc are the same as explained in Brands CRUD tutorial)
All other fields are simple. Please note how we added the repeater field (id = order_lines). All the repeater fields (product, quantity, price, taxes, total) have their parent_id set to the id of repeater i.e. order_lines
Also, note that we added custom HTML for showing order TOTALS.
...
public function __construct()
{
...
$this->addFormFields()
...
}
public function addFormFields()
{
$this->addFormItem([
'id' => 'main-row',
'type' => 'div',
'class' => 'row'
]);
$this->addFormItem([
'id' => 'left-col',
'parent_id' => 'main-row',
'type' => 'div',
'class' => 'col-md-6'
]);
$this->addFormItem([
'id' => 'right-col',
'parent_id' => 'main-row',
'type' => 'div',
'class' => 'col-md-6'
]);
$this->addFormItem([
'id' => 'date',
'parent_id' => 'left-col',
'type' => 'datetime',
'enable_time' => true,
'validation_rules' => [
'date' => 'required|date'
],
'label' => __('Date'),
'name' => 'date'
]);
$this->addFormItem([
'id' => 'customer_name',
'parent_id' => 'left-col',
'type' => 'text',
'validation_rules' => [
'customer_name' => 'required'
],
'label' => __('Customer Name'),
'name' => 'customer_name'
]);
$this->addFormItem([
'id' => 'shipping_address',
'parent_id' => 'right-col',
'type' => 'textarea',
'validation_rules' => [
'shipping_address' => 'required'
],
'label' => __('Shipping Address'),
'name' => 'shipping_address'
]);
$this->addFormItem([
'id' => 'order_lines',
'parent_id' => null,
'type' => 'repeater',
'relation_name' => 'orderLines',
'model' => '\App\Models\OrderLine',
'label' => __('Order Lines'),
'table_view' => true,
]);
$this->addFormItem([
'id' => 'product',
'parent_id' => 'order_lines',
'type' => 'belongsTo',
'relation_name' => 'product',
'model' => '\App\Models\Product',
'where' => function($query){
return $query->where('is_active', 1);
},
'validation_rules' => [
'product' => 'required|array',
'product.*' => 'required'
],
'label' => __('Product'),
'name' => 'product',
'show_add_new_button' => true,
]);
$this->addFormItem([
'id' => 'quantity',
'parent_id' => 'order_lines',
'type' => 'decimal',
'validation_rules' => [
'quantity' => 'required|array',
'quantity.*' => 'required|numeric'
],
'label' => __('Quantity'),
'name' => 'quantity'
]);
$this->addFormItem([
'id' => 'price',
'parent_id' => 'order_lines',
'type' => 'decimal',
'validation_rules' => [
'price' => 'required|array',
'price.*' => 'required|numeric'
],
'readonly' => true,
'label' => __('Price'),
'name' => 'price'
]);
$this->addFormItem([
'id' => 'taxes',
'parent_id' => 'order_lines',
'type' => 'belongsToMany',
'relation_name' => 'taxes',
'model' => '\App\Models\Tax',
'label' => __('Taxes'),
'name' => 'taxes',
'show_add_new_button' => true,
]);
$this->addFormItem([
'id' => 'instruction',
'parent_id' => 'order_lines_instructions',
'type' => 'text',
'validation_rules' => [
'instruction' => 'required|array'
],
'label' => __('Instructions'),
'name' => 'instruction'
]);
$this->addFormItem([
'id' => 'total',
'parent_id' => 'order_lines',
'type' => 'decimal',
'label' => __('Total'),
'readonly' => true,
'display_only' => true,
'name' => 'total'
]);
// ADD SOME CUSTOM HTML
// This html is for showing order TOTALS
$this->addFormItem([
'id' => 'order_totals',
'parent_id' => null,
'type' => 'html',
'html' => <<<EOL
<hr>
<div class="row">
<div class="col-md-6 offset-md-6">
<table class="table totals">
<tbody>
<tr>
<td>Excluding Tax</td>
<td><input class="form-control excluding-tax" readonly></td>
</tr>
<tr>
<td>Taxes</td>
<td><input class="form-control taxes" readonly></td>
</tr>
<tr>
<td>Total</td>
<td><input class="form-control total" readonly></td>
</tr>
</tbody>
</table>
</div>
</div>
EOL
]);
}
...
Adding javascript to forms is simple. Please see the following sample code
public function __construct()
{
...
$this->addJavascript()
...
}
public function addJavascript()
{
$this->addFormItem([
'id' => 'some_script_id',
'parent_id' => null,
'type' => 'html',
'html' => <<<EOL
<script>
(function () {
// This is current form object
let form = document.currentScript.closest('form');
// ADD YOUR SCRIPT HERE
})();
</script>
EOL
]);
}
public function __construct()
{
...
$this->addJavascript()
...
}
public function addJavascript()
{
$this->addFormItem([
'id' => 'order_lines_script',
'parent_id' => null,
'type' => 'html',
'html' => <<<EOL
<script>
(function () {
let form = document.currentScript.closest('form');
// SCRIPT CODE STARTS HERE
$(form).on('select2:select', function (e) {
let input = e.target
var data = e.params.data;
if( input.name.startsWith('product[') )
{
let priceInput = $(input).closest('tr').find('[name^="price["]');
priceInput.val(data.price);
}
});
$(form).on('select2:clear', function (e) {
let input = e.target
if(input.name.startsWith('product[') )
{
let priceInput = $(input).closest('tr').find('[name^="price["]');
priceInput.val(0);
}
});
function calculateOrderTotals(orderForm){
var taxesGrandTotal = 0;
var grandTotal = 0;
var trs = orderForm.querySelectorAll('table.order_lines tbody tr:not(.template)');
for(var i = 0; i < trs.length; i++)
{
var tr = trs[i];
var priceInput = tr.querySelector('[name^="price["]')
var price = priceInput ? priceInput.value : 0;
var quantityInput = tr.querySelector('[name^="quantity["]');
var quantity = quantityInput ? quantityInput.value : 0;
var beforeTax = price * quantity;
var taxes = $(tr).find('[name^="taxes["]').select2('data');
var calculatedTax = 0;
if (taxes) {
for(var j = 0; j < taxes.length; j++)
{
var tax = taxes[j];
calculatedTax += beforeTax * tax.rate / 100;
}
}
taxesGrandTotal += calculatedTax;
grandTotal += beforeTax + calculatedTax;
tr.querySelector('[name^="total["]').value = beforeTax + calculatedTax;
}
document.querySelector('table.totals .excluding-tax').value = grandTotal - taxesGrandTotal;
document.querySelector('table.totals .taxes').value = taxesGrandTotal;
document.querySelector('table.totals .total').value = grandTotal;
}
var intervalId = setInterval(() => {
if (!document.body.contains(form))
{
clearInterval(intervalId);
}
calculateOrderTotals(form);
}, 1000);
// SCRIPT CODE ENDS HERE
})();
</script>
EOL
]);
}