migrate to gtea from bistbucket
This commit is contained in:
413
public/assets/js/custom/account.js
Normal file
413
public/assets/js/custom/account.js
Normal file
@@ -0,0 +1,413 @@
|
||||
"use strict";
|
||||
|
||||
// Toggle extra input fields in the "Add New Bank" modal
|
||||
$(".more-field-btn").on("click", function () {
|
||||
const $extraFields = $(".extra-fields");
|
||||
const $toggleBtn = $(this);
|
||||
|
||||
if ($extraFields.hasClass("hidden-fields")) {
|
||||
$extraFields.removeClass("hidden-fields");
|
||||
$toggleBtn.text("- Hide fields");
|
||||
} else {
|
||||
$extraFields.addClass("hidden-fields");
|
||||
$toggleBtn.text("+ Add more fields");
|
||||
}
|
||||
});
|
||||
|
||||
/** Dynamically Bank Transaction Modal Open start */
|
||||
// Fetch banks
|
||||
function fetchBanks(callback) {
|
||||
var url = $('#get_banks').val();
|
||||
$.get(url, function(data) {
|
||||
var options = '<option value="">Select one</option>';
|
||||
data.forEach(function(bank) {
|
||||
options += `<option value="${bank.id}">${bank.name}</option>`;
|
||||
});
|
||||
callback(options);
|
||||
});
|
||||
}
|
||||
|
||||
// Open modal dynamically & build From/To fields based on type
|
||||
function openTransactionModal(type, transaction = null, route = null) {
|
||||
var $modal = $('#bankTransactionModal');
|
||||
var $title = $('#transactionModalTitle');
|
||||
var $modalBody = $modal.find('.modal-body .row');
|
||||
var $form = $modal.find('form');
|
||||
|
||||
// Remove old dynamic fields
|
||||
$modalBody.find('.dynamic-field').remove();
|
||||
|
||||
// transaction_type input field
|
||||
$modal.find('input[name="transaction_type"]').remove();
|
||||
$form.append(`<input type="hidden" name="transaction_type" value="${type}">`);
|
||||
|
||||
// Set form action & method
|
||||
if (transaction && route) {
|
||||
// Edit mode
|
||||
$form.attr('action', route);
|
||||
if ($form.find('input[name="_method"]').length === 0) {
|
||||
$form.append('<input type="hidden" name="_method" value="PUT">');
|
||||
} else {
|
||||
$form.find('input[name="_method"]').val('PUT');
|
||||
}
|
||||
} else {
|
||||
// Create mode
|
||||
$form.attr('action', route);
|
||||
$form.find('input[name="_method"]').remove();
|
||||
}
|
||||
|
||||
// Fetch banks and build fields
|
||||
fetchBanks(function(options){
|
||||
var content = '';
|
||||
var titleText = '';
|
||||
|
||||
if (type === 'bank_to_bank') {
|
||||
titleText = 'Bank to Bank Transfer';
|
||||
content = `
|
||||
<div class="col-lg-6 mb-2 dynamic-field">
|
||||
<label>From</label>
|
||||
<select name="from" class="form-control table-select w-100 from" required>${options}</select>
|
||||
</div>
|
||||
<div class="col-lg-6 mb-2 dynamic-field">
|
||||
<label>To</label>
|
||||
<select name="to" class="form-control table-select w-100 to" required>${options}</select>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (type === 'bank_to_cash') {
|
||||
titleText = 'Bank to Cash Transfer';
|
||||
content = `
|
||||
<div class="col-lg-6 mb-2 dynamic-field">
|
||||
<label>From</label>
|
||||
<select name="from" class="form-control table-select w-100 from" required>${options}</select>
|
||||
</div>
|
||||
<div class="col-lg-6 mb-2 dynamic-field">
|
||||
<label>To</label>
|
||||
<input type="text" readonly value="Cash" class="form-control to">
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (type === 'adjust_bank') {
|
||||
titleText = 'Adjust Bank Balance';
|
||||
content = `
|
||||
<div class="col-lg-6 mb-2 dynamic-field">
|
||||
<label>Account Name</label>
|
||||
<select name="from" class="form-control table-select w-100 from" required>${options}</select>
|
||||
</div>
|
||||
<div class="col-lg-6 mb-2 dynamic-field">
|
||||
<label>Type</label>
|
||||
<select name="type" class="form-control table-select w-100 type" required>
|
||||
<option value="credit">Increase balance</option>
|
||||
<option value="debit">Decrease balance</option>
|
||||
</select>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (type === 'cash_to_bank') {
|
||||
titleText = 'Cash To Bank Transfer';
|
||||
content = `
|
||||
<div class="col-lg-6 mb-2 dynamic-field">
|
||||
<label>From</label>
|
||||
<input type="text" readonly name="from" value="Cash" class="form-control from">
|
||||
</div>
|
||||
<div class="col-lg-6 mb-2 dynamic-field">
|
||||
<label>To</label>
|
||||
<select name="to" class="form-control table-select w-100 to" required>${options}</select>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (type === 'adjust_cash') {
|
||||
titleText = 'Adjust Cash Balance';
|
||||
|
||||
var creditChecked = transaction && transaction.type === 'credit' ? 'checked' : '';
|
||||
var debitChecked = transaction && transaction.type === 'debit' ? 'checked' : '';
|
||||
|
||||
content = `
|
||||
<div class="col-lg-12 mb-3 dynamic-field">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="type" id="addCash" value="credit" ${creditChecked}>
|
||||
<label class="form-check-label cash-label" for="addCash">
|
||||
Add Cash
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="type" id="reduceCash" value="debit" ${debitChecked}>
|
||||
<label class="form-check-label cash-label" for="reduceCash">
|
||||
Reduce Cash
|
||||
</label>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Insert dynamic fields
|
||||
$modalBody.prepend(content);
|
||||
|
||||
// Set modal title
|
||||
$title.text(titleText);
|
||||
|
||||
if (transaction) {
|
||||
$modal.find('.from').val(transaction.from);
|
||||
$modal.find('.type').val(transaction.type);
|
||||
$modal.find('.amount').val(transaction.amount);
|
||||
$modal.find('.date').val(transaction.date);
|
||||
$modal.find('.note').val(transaction.note);
|
||||
|
||||
// Special handling for To field
|
||||
if (type === 'bank_to_cash') {
|
||||
$modal.find('.to').val('Cash');
|
||||
} else if (type === 'cash_to_bank') {
|
||||
$modal.find('.to').val(transaction.to);
|
||||
} else {
|
||||
$modal.find('.to').val(transaction.to);
|
||||
}
|
||||
|
||||
if(transaction.image) {
|
||||
$('#customPreviewImg').attr('src', transaction.image).show();
|
||||
$('#customUploadContent').hide();
|
||||
} else {
|
||||
$('#customPreviewImg').hide();
|
||||
$('#customUploadContent').show();
|
||||
}
|
||||
}
|
||||
|
||||
// Show modal
|
||||
new bootstrap.Modal($modal[0]).show();
|
||||
});
|
||||
}
|
||||
|
||||
// Create buttons
|
||||
$('#bank_to_bank, #bank_to_cash, #adjust_bank, #cash_to_bank, #adjust_cash').on('click', function() {
|
||||
var type = this.id;
|
||||
var route = $(this).data('route');
|
||||
|
||||
openTransactionModal(type, null, route);
|
||||
});
|
||||
|
||||
// Edit buttons
|
||||
$(document).on('click', '.edit-transaction', function() {
|
||||
var transactionType = $(this).data('transaction_type');
|
||||
var transaction = {
|
||||
amount: $(this).data('amount'),
|
||||
date: $(this).data('date'),
|
||||
note: $(this).data('note'),
|
||||
type: $(this).data('type'),
|
||||
image: $(this).data('image')
|
||||
};
|
||||
|
||||
// handling for cash transactions
|
||||
if(transactionType === 'cash_to_bank') {
|
||||
transaction.from = 'Cash';
|
||||
transaction.to = $(this).data('to'); // bank id
|
||||
} else if(transactionType === 'adjust_cash') {
|
||||
transaction.type = $(this).data('type'); // credit/debit
|
||||
} else {
|
||||
// bank transactions
|
||||
transaction.from = $(this).data('from');
|
||||
transaction.to = $(this).data('to');
|
||||
}
|
||||
|
||||
var route = $(this).data('route');
|
||||
|
||||
openTransactionModal(transactionType, transaction, route);
|
||||
});
|
||||
|
||||
/** Dynamically Bank Transaction Modal Open end */
|
||||
|
||||
// Cheque transfer start
|
||||
$(document).on("click", ".cheque-transfer-btn", function () {
|
||||
var received_from = $(this).data("received-from");
|
||||
var cheque_amount = $(this).data("cheque-amount");
|
||||
var cheque_no = $(this).data("cheque-no");
|
||||
var cheque_date = $(this).data("cheque-date");
|
||||
var ref_no = $(this).data("ref-no");
|
||||
var transaction_id = $(this).data("transaction-id");
|
||||
|
||||
$("#received_from").text(received_from);
|
||||
$("#cheque_amount").text(cheque_amount);
|
||||
$("#cheque_no").text(cheque_no);
|
||||
$("#cheque_date").text(cheque_date);
|
||||
$("#ref_no").text(ref_no);
|
||||
$("#deposit_transaction_id").val(transaction_id);
|
||||
});
|
||||
// Cheque transfer end
|
||||
|
||||
// When clicking Re-Open button
|
||||
$(document).on("click", ".reopen-btn", function () {
|
||||
|
||||
let transaction_id = $(this).data("transaction-id");
|
||||
|
||||
$("#reopen_transaction_id").val(transaction_id);
|
||||
|
||||
let $modalEl = $("#reopen");
|
||||
let modal = new bootstrap.Modal($modalEl[0]);
|
||||
modal.show();
|
||||
});
|
||||
|
||||
// Bank Accounts Img upload ----------->
|
||||
// Bank Accounts Img upload ----------->
|
||||
$(document).ready(function () {
|
||||
const $fileInput = $("#customFileInput");
|
||||
const $previewImg = $("#customPreviewImg");
|
||||
const $content = $("#customUploadContent");
|
||||
|
||||
$fileInput.on("change", function () {
|
||||
const file = this.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
$previewImg.attr("src", e.target.result).show();
|
||||
$content.hide();
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Bank edit start
|
||||
$(document).on("click", ".bank-edit-btn", function () {
|
||||
var url = $(this).data("url");
|
||||
var name = $(this).data("name");
|
||||
var opening_balance = $(this).data("opening-balance");
|
||||
var opening_date = $(this).data("opening-date");
|
||||
var account_number = $(this).data("account-number");
|
||||
var routing_number = $(this).data("routing-number");
|
||||
var upi_id = $(this).data("upi-id");
|
||||
var bank_name = $(this).data("bank-name");
|
||||
var branch_name = $(this).data("branch-name");
|
||||
var account_holder = $(this).data("account-holder");
|
||||
var show_in_invoice = $(this).data("show-in-invoice");
|
||||
|
||||
$("#name").val(name);
|
||||
$("#opening_balance").val(opening_balance);
|
||||
$("#opening_date").val(opening_date);
|
||||
$("#account_number").val(account_number);
|
||||
$("#routing_number").val(routing_number);
|
||||
$("#upi_id").val(upi_id);
|
||||
$("#bank_name").val(bank_name);
|
||||
$("#branch_name").val(branch_name);
|
||||
$("#account_holder").val(account_holder);
|
||||
$("#show_in_invoice").prop('checked', show_in_invoice === 1);
|
||||
|
||||
$(".bankUpdateForm").attr("action", url);
|
||||
});
|
||||
// Bank edit end
|
||||
|
||||
// Bank view start
|
||||
$(document).on("click", ".bank-view-btn", function () {
|
||||
var name = $(this).data("name");
|
||||
var balance = $(this).data("balance");
|
||||
var opening_date = $(this).data("opening-date");
|
||||
var account_number = $(this).data("account-number");
|
||||
var bank_name = $(this).data("bank-name");
|
||||
var branch_name = $(this).data("branch-name");
|
||||
var routing_number = $(this).data("routing-number");
|
||||
var upi_id = $(this).data("upi-id");
|
||||
var account_holder = $(this).data("account-holder");
|
||||
var show_in_invoice = $(this).data("show-in-invoice");
|
||||
|
||||
$("#view_account_name").text(name);
|
||||
$("#view_balance").text(balance);
|
||||
$("#view_opening_date").text(opening_date);
|
||||
$("#view_account_number").text(account_number);
|
||||
$("#view_bank_name").text(bank_name);
|
||||
$("#view_branch_name").text(branch_name);
|
||||
$("#view_routing_number").text(routing_number);
|
||||
$("#view_upi_id").text(upi_id);
|
||||
$("#view_account_holder").text(account_holder);
|
||||
$("#view_show_in_invoice").text(show_in_invoice == 1 ? 'Yes' : 'No');
|
||||
});
|
||||
// Bank view end
|
||||
|
||||
// Bank Delete Start
|
||||
$(document).on("click", ".delete-bank-action", function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
let url = $(this).data("action") ?? $(this).attr("href");
|
||||
let method = $(this).data("method") ?? "DELETE";
|
||||
let icon = $(this).data("icon") ?? "fas fa-trash-alt";
|
||||
|
||||
$.confirm({
|
||||
title: "Are you sure?",
|
||||
icon: icon,
|
||||
theme: "modern",
|
||||
closeIcon: true,
|
||||
animation: "scale",
|
||||
type: "red",
|
||||
buttons: {
|
||||
confirm: {
|
||||
btnClass: "btn-red",
|
||||
action: function () {
|
||||
$.ajax({
|
||||
type: method,
|
||||
url: url,
|
||||
success: function (response) {
|
||||
if (response === false) {
|
||||
$("#warning-modal").modal("show");
|
||||
}
|
||||
else if (response.redirect) {
|
||||
window.sessionStorage.hasPreviousMessage = true;
|
||||
window.sessionStorage.previousMessage =
|
||||
response.message ?? null;
|
||||
location.href = response.redirect;
|
||||
}
|
||||
else {
|
||||
Notify("success", response.message || response);
|
||||
}
|
||||
},
|
||||
error: function (xhr) {
|
||||
Notify(
|
||||
"error",
|
||||
xhr.responseJSON?.message || "Something went wrong!"
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
close: {
|
||||
action: function () {
|
||||
this.buttons.close.hide();
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
// Bank Delete Start
|
||||
|
||||
// Transaction view start
|
||||
$(document).on("click", ".transaction-view-btn", function () {
|
||||
var img = $(this).data("img");
|
||||
var user = $(this).data("user");
|
||||
var transaction_type = $(this).data("transaction-type");
|
||||
var amount = $(this).data("amount");
|
||||
var date = $(this).data("date");
|
||||
var note = $(this).data("note");
|
||||
|
||||
$("#view_img").attr("src", img);
|
||||
$("#view_user").text(user);
|
||||
$("#view_transaction_type").text(transaction_type);
|
||||
$("#view_amount").text(amount);
|
||||
$("#view_date").text(date);
|
||||
$("#view_note").text(note);
|
||||
});
|
||||
// Transaction view end
|
||||
|
||||
// Cash Transaction view start
|
||||
$(document).on("click", ".cash-view-btn", function () {
|
||||
var img = $(this).data("img");
|
||||
var user = $(this).data("user");
|
||||
var transaction_type = $(this).data("transaction-type");
|
||||
var platform = $(this).data("platform");
|
||||
var amount = $(this).data("amount");
|
||||
var note = $(this).data("note");
|
||||
var date = $(this).data("date");
|
||||
|
||||
$("#view_img").attr("src", img);
|
||||
$("#view_user").text(user);
|
||||
$("#view_transaction_type").text(transaction_type);
|
||||
$("#view_platform").text(platform);
|
||||
$("#view_amount").text(amount);
|
||||
$("#view_note").text(note);
|
||||
$("#view_date").text(date);
|
||||
});
|
||||
// Cash Transaction view end
|
||||
|
||||
80
public/assets/js/custom/calculator.js
Normal file
80
public/assets/js/custom/calculator.js
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const display = document.getElementById("display");
|
||||
let currentExpression = "";
|
||||
|
||||
|
||||
function buttonClickHandler(event) {
|
||||
const buttonValue = event.target.innerText;
|
||||
|
||||
|
||||
if (buttonValue === "C") {
|
||||
currentExpression = "";
|
||||
}
|
||||
|
||||
else if (buttonValue === "⌫") {
|
||||
currentExpression = currentExpression.slice(0, -1);
|
||||
}
|
||||
|
||||
else if (buttonValue === "=") {
|
||||
try {
|
||||
currentExpression = calculate(currentExpression).toString();
|
||||
} catch {
|
||||
currentExpression = "Error";
|
||||
}
|
||||
}
|
||||
|
||||
else if (buttonValue === "√") {
|
||||
if (!currentExpression) {
|
||||
currentExpression = "Error";
|
||||
} else {
|
||||
currentExpression = Math.sqrt(parseFloat(currentExpression)).toString();
|
||||
}
|
||||
}
|
||||
|
||||
else if (buttonValue === "x²") {
|
||||
if (!currentExpression) {
|
||||
currentExpression = "Error";
|
||||
} else {
|
||||
currentExpression = Math.pow(parseFloat(currentExpression), 2).toString();
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if (currentExpression === "Error") {
|
||||
currentExpression = "";
|
||||
}
|
||||
currentExpression += buttonValue;
|
||||
}
|
||||
|
||||
|
||||
display.value = currentExpression;
|
||||
}
|
||||
|
||||
|
||||
function calculate(expression) {
|
||||
|
||||
expression = expression
|
||||
.replace(/×/g, "*")
|
||||
.replace(/÷/g, "/")
|
||||
.replace(/−/g, "-");
|
||||
|
||||
|
||||
try {
|
||||
const result = Function(`return (${expression})`)();
|
||||
if (result === Infinity || result === -Infinity || isNaN(result)) {
|
||||
throw new Error("Math Error");
|
||||
}
|
||||
return result;
|
||||
} catch {
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const calculatorButtons = document.querySelectorAll(".button-row button");
|
||||
calculatorButtons.forEach(button => {
|
||||
button.addEventListener("click", buttonClickHandler);
|
||||
});
|
||||
});
|
||||
8
public/assets/js/custom/custom-alart.js
Normal file
8
public/assets/js/custom/custom-alart.js
Normal file
@@ -0,0 +1,8 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const alertBox = document.getElementById("alertBox");
|
||||
const closeBtn = document.querySelector(".alert-close-btn");
|
||||
|
||||
closeBtn.addEventListener("click", () => {
|
||||
alertBox.style.display = "none";
|
||||
});
|
||||
});
|
||||
3032
public/assets/js/custom/custom.js
Normal file
3032
public/assets/js/custom/custom.js
Normal file
File diff suppressed because it is too large
Load Diff
33
public/assets/js/custom/custome-dropdown.js
Normal file
33
public/assets/js/custom/custome-dropdown.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const dropdown = document.getElementById("myDropdown");
|
||||
|
||||
if (dropdown) {
|
||||
const selected = dropdown.querySelector("#selectedOption");
|
||||
const options = dropdown.querySelector("#dropdownOptions");
|
||||
const arrow = dropdown.querySelector("#dropdownArrow");
|
||||
const items = dropdown.querySelectorAll(".dropdown-option");
|
||||
|
||||
const batchInput = document.getElementById("selected_batch_no");
|
||||
|
||||
selected.addEventListener("click", () => {
|
||||
const isOpen = options.style.display === "block";
|
||||
options.style.display = isOpen ? "none" : "block";
|
||||
dropdown.classList.toggle("open", !isOpen);
|
||||
});
|
||||
|
||||
items.forEach((item) => {
|
||||
item.addEventListener("click", () => {
|
||||
selected.querySelector("span").innerHTML = item.innerHTML;
|
||||
batchInput.value = item.getAttribute("data-batch-no");
|
||||
|
||||
options.style.display = "none";
|
||||
dropdown.classList.remove("open");
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("click", function (e) {
|
||||
if (!dropdown.contains(e.target)) {
|
||||
options.style.display = "none";
|
||||
dropdown.classList.remove("open");
|
||||
}
|
||||
});
|
||||
}
|
||||
55
public/assets/js/custom/math.min.js
vendored
Normal file
55
public/assets/js/custom/math.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1103
public/assets/js/custom/product.js
Normal file
1103
public/assets/js/custom/product.js
Normal file
File diff suppressed because it is too large
Load Diff
620
public/assets/js/custom/purchase.js
Normal file
620
public/assets/js/custom/purchase.js
Normal file
@@ -0,0 +1,620 @@
|
||||
$(document).ready(function () {
|
||||
var $sidebarPlan = $(".lg-sub-plan");
|
||||
var $subPlan = $(".sub-plan");
|
||||
var isActive = $(window).width() >= 1150;
|
||||
|
||||
$(".side-bar, .section-container").toggleClass("active", isActive);
|
||||
|
||||
if (isActive) {
|
||||
$sidebarPlan.hide();
|
||||
$subPlan.show();
|
||||
|
||||
$(".side-bar-addon, .side-bar-addon-2, .side-bar-addon-3").hide();
|
||||
} else {
|
||||
$sidebarPlan.show();
|
||||
$subPlan.hide();
|
||||
|
||||
$(".side-bar-addon, .side-bar-addon-2, .side-bar-addon-3").show();
|
||||
}
|
||||
});
|
||||
|
||||
// get number only
|
||||
function getNumericValue(value) {
|
||||
return parseFloat(value.replace(/[^0-9.-]+/g, "")) || 0;
|
||||
}
|
||||
|
||||
// Update the cart list and call the callback once complete
|
||||
function fetchUpdatedCart(callback) {
|
||||
let url = $("#purchase-cart").val();
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "GET",
|
||||
success: function (response) {
|
||||
$("#purchase_cart_list").html(response);
|
||||
if (typeof callback === "function") callback(); // Call the callback after updating the cart
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
//increase quantity
|
||||
$(document).on("click", ".plus-btn", function (e) {
|
||||
e.preventDefault();
|
||||
let $row = $(this).closest("tr");
|
||||
let rowId = $row.data("row_id");
|
||||
let updateRoute = $row.data("update_route");
|
||||
let $qtyInput = $row.find(".cart-qty");
|
||||
let currentQty = parseFloat($qtyInput.val());
|
||||
|
||||
let newQty = currentQty + 1;
|
||||
$qtyInput.val(newQty);
|
||||
updateCartQuantity(rowId, newQty, updateRoute);
|
||||
});
|
||||
|
||||
//decrease quantity
|
||||
$(document).on("click", ".minus-btn", function (e) {
|
||||
e.preventDefault();
|
||||
let $row = $(this).closest("tr");
|
||||
let rowId = $row.data("row_id");
|
||||
let updateRoute = $row.data("update_route");
|
||||
let $qtyInput = $row.find(".cart-qty");
|
||||
let currentQty = parseFloat($qtyInput.val());
|
||||
|
||||
// Ensure quantity does not go below 1
|
||||
if (currentQty > 1) {
|
||||
let newQty = currentQty - 1;
|
||||
$qtyInput.val(newQty);
|
||||
updateCartQuantity(rowId, newQty, updateRoute);
|
||||
}
|
||||
});
|
||||
|
||||
// Cart quantity input field change event
|
||||
$(document).on("change", ".cart-qty", function () {
|
||||
let $row = $(this).closest("tr");
|
||||
let rowId = $row.data("row_id");
|
||||
let updateRoute = $row.data("update_route");
|
||||
let newQty = parseFloat($(this).val());
|
||||
|
||||
// Ensure quantity does not go below 1
|
||||
if (newQty >= 0) {
|
||||
updateCartQuantity(rowId, newQty, updateRoute);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove item from the cart
|
||||
$(document).on("click", ".remove-btn", function (e) {
|
||||
e.preventDefault();
|
||||
var $row = $(this).closest("tr");
|
||||
var destroyRoute = $row.data("destroy_route");
|
||||
|
||||
$.ajax({
|
||||
url: destroyRoute,
|
||||
type: "DELETE",
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
// Item was successfully removed, fade out and remove the row from DOM
|
||||
$row.fadeOut(400, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
// Recalculate and update cart totals
|
||||
fetchUpdatedCart(calTotalAmount);
|
||||
} else {
|
||||
toastr.error(response.message || "Failed to remove item");
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
toastr.error("Error removing item from cart");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Function to update cart item with the new quantity
|
||||
function updateCartQuantity(rowId, newQty, updateRoute) {
|
||||
$.ajax({
|
||||
url: updateRoute,
|
||||
type: "PUT",
|
||||
data: {
|
||||
rowId: rowId,
|
||||
qty: newQty,
|
||||
},
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
fetchUpdatedCart(calTotalAmount); // Re-fetch the cart and recalculate the total amount
|
||||
} else {
|
||||
toastr.error(response.message || "Failed to update quantity");
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
toastr.error("Error updating cart quantity");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Clear the cart and then refresh the UI with updated values
|
||||
function clearCart(cartType) {
|
||||
let route = $("#clear-cart").val();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: route,
|
||||
data: {
|
||||
type: cartType,
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
fetchUpdatedCart(calTotalAmount); // Call calTotalAmount after cart fetch completes
|
||||
},
|
||||
error: function () {
|
||||
console.error("There was an issue clearing the cart.");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** Add to cart start **/
|
||||
|
||||
let selectedProduct = {};
|
||||
|
||||
$(document).on("click", "#single-product", function () {
|
||||
showProductModal($(this));
|
||||
});
|
||||
|
||||
function autoOpenModal(id) {
|
||||
let element = $("#products-list").find(".single-product." + id);
|
||||
showProductModal(element);
|
||||
}
|
||||
|
||||
function showProductModal(element) {
|
||||
selectedProduct = {
|
||||
product_id: element.data("product_id"),
|
||||
product_code: element.data("product_code"),
|
||||
product_unit_id: element.data("product_unit_id"),
|
||||
product_unit_name: element.data("product_unit_name"),
|
||||
product_image: element.data("product_image"),
|
||||
product_name: element.find(".product_name").text(),
|
||||
brand: element.data("brand"),
|
||||
stock: element.data("stock"),
|
||||
purchase_price: element.data("purchase_price"),
|
||||
sales_price: element.data("sales_price"),
|
||||
whole_sale_price: element.data("whole_sale_price"),
|
||||
dealer_price: element.data("dealer_price"),
|
||||
product_type: element.data("product_type"),
|
||||
vat_id: element.data("vat_id"),
|
||||
vat_rate: element.data("vat_rate"),
|
||||
};
|
||||
|
||||
// Set modal display values
|
||||
$("#product_name").text(selectedProduct.product_name);
|
||||
$("#brand").text(selectedProduct.brand);
|
||||
$("#stock").text(selectedProduct.stock);
|
||||
$("#purchase_price").val(selectedProduct.purchase_price);
|
||||
$("#sales_price").val(selectedProduct.sales_price);
|
||||
$("#whole_sale_price").val(selectedProduct.whole_sale_price);
|
||||
$("#dealer_price").val(selectedProduct.dealer_price);
|
||||
$("#batch_no").val("");
|
||||
|
||||
// show/hide variant field according to Product Type
|
||||
if (selectedProduct.product_type === "variant") {
|
||||
$("#variant_name").closest(".col-lg-6").removeClass("d-none");
|
||||
$("#variant_name").prop("disabled", false);
|
||||
loadVariantOptions(selectedProduct.product_id);
|
||||
} else {
|
||||
$("#variant_name").closest(".col-lg-6").addClass("d-none");
|
||||
$("#variant_name").prop("disabled", true);
|
||||
$("#variant_name").html('<option value="">Select Variant</option>');
|
||||
}
|
||||
|
||||
// Read expire_date_type safely from hidden input
|
||||
const settingExpireType = $("#setting_expire_type").val() || "dmy";
|
||||
const $expireInput = $("#expire_date");
|
||||
|
||||
if ($expireInput.length) {
|
||||
if (settingExpireType === "my") {
|
||||
$expireInput.attr("type", "month");
|
||||
} else {
|
||||
$expireInput.attr("type", "date");
|
||||
}
|
||||
}
|
||||
|
||||
$("#product-modal").modal("show");
|
||||
}
|
||||
|
||||
$(".product-filter").on("submit", function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
let savingLoader = '<div class="spinner-border spinner-border-sm custom-text-primary" role="status"><span class="visually-hidden">Loading...</span></div>',
|
||||
$purchase_modal_reload = $("#purchase_modal");
|
||||
$purchase_modal_reload.initFormValidation(),
|
||||
// item modal action
|
||||
$("#purchase_modal").on("submit", function (e) {
|
||||
e.preventDefault();
|
||||
let t = $(this).find(".submit-btn"),
|
||||
a = t.html();
|
||||
let url = $(this).data("route");
|
||||
let quantity = parseFloat($("#product_qty").val());
|
||||
$purchase_modal_reload.valid() &&
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
data: {
|
||||
type: "purchase",
|
||||
id: selectedProduct.product_id,
|
||||
name: selectedProduct.product_name,
|
||||
quantity: quantity,
|
||||
price: parseFloat($("#purchase_price").val()) || selectedProduct.purchase_price,
|
||||
sales_price: parseFloat($("#sales_price").val()),
|
||||
whole_sale_price: parseFloat($("#whole_sale_price").val()),
|
||||
dealer_price: parseFloat($("#dealer_price").val()),
|
||||
batch_no: $("#batch_no").val(),
|
||||
expire_date: $("#expire_date").val(),
|
||||
product_code: selectedProduct.product_code,
|
||||
product_unit_id: selectedProduct.product_unit_id,
|
||||
product_unit_name: selectedProduct.product_unit_name,
|
||||
product_image: selectedProduct.product_image,
|
||||
warehouse_id: $("#warehouse_id").val(),
|
||||
variant_name: $("#variant_name").val(),
|
||||
vat_id: selectedProduct.vat_id,
|
||||
vat_rate: selectedProduct.vat_rate
|
||||
},
|
||||
beforeSend: function () {
|
||||
t.html(savingLoader).attr("disabled", !0);
|
||||
},
|
||||
success: function (response) {
|
||||
t.html(a).removeClass("disabled").attr("disabled", !1);
|
||||
|
||||
if (response.success) {
|
||||
fetchUpdatedCart(calTotalAmount); // Update totals after cart fetch completes
|
||||
$("#product-modal").modal("hide");
|
||||
$("#product_qty").val("");
|
||||
$("#variant_name").val("");
|
||||
$("#warehouse_id").val("");
|
||||
$("#expire_date").val("");
|
||||
} else {
|
||||
toastr.error(
|
||||
response.message || "Failed to add product to cart."
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function (xhr) {
|
||||
toastr.error(
|
||||
"An error occurred while adding product to cart."
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
/** Add to cart End **/
|
||||
|
||||
// Function to load variant options
|
||||
function loadVariantOptions(product_id) {
|
||||
let url = $("#get-product-variants").val() + '/' + product_id;
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "GET",
|
||||
success: function (res) {
|
||||
let options = '<option value="">Select Variant</option>';
|
||||
res.forEach(function (variant) {
|
||||
// Store batch_no as data attribute
|
||||
options += `<option value="${variant.variant_name}" data-batch="${variant.batch_no || ''}" data-expire_date="${variant.expire_date || ''}">
|
||||
${variant.variant_name} (${variant.batch_no || '-'})
|
||||
</option>`;
|
||||
});
|
||||
$("#variant_name").html(options);
|
||||
|
||||
// Clear field when product modal opens
|
||||
$("#batch_no").val('');
|
||||
$("#expire_date").val('');
|
||||
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// variant options on change event
|
||||
$(document).on("change", "#variant_name", function () {
|
||||
let selectedOption = $(this).find(":selected");
|
||||
|
||||
let batchNo = selectedOption.data("batch") || '';
|
||||
$("#batch_no").val(batchNo);
|
||||
|
||||
let expireDate = selectedOption.data("expire_date") || '';
|
||||
|
||||
// Adjust format if type=month
|
||||
const $expireInput = $("#expire_date");
|
||||
if ($expireInput.attr("type") === "month" && expireDate) {
|
||||
expireDate = expireDate.slice(0, 7); // yyyy-MM
|
||||
}
|
||||
|
||||
$expireInput.val(expireDate);
|
||||
});
|
||||
|
||||
// Trigger calculation whenever Discount, or Receive Amount fields change
|
||||
$("#discount_amount, #receive_amount, #shipping_charge").on(
|
||||
"input",
|
||||
function () {
|
||||
calTotalAmount();
|
||||
}
|
||||
);
|
||||
|
||||
// vat calculation
|
||||
$(".vat_select").on("change", function () {
|
||||
let vatRate = parseFloat($(this).find(":selected").data("rate")) || 0;
|
||||
let subtotal = getNumericValue($("#sub_total").text()) || 0;
|
||||
|
||||
let vatAmount = (subtotal * vatRate) / 100;
|
||||
|
||||
$("#vat_amount").val(vatAmount.toFixed(2));
|
||||
calTotalAmount();
|
||||
});
|
||||
|
||||
// discount calculation
|
||||
$(".discount_type").on("change", function () {
|
||||
calTotalAmount();
|
||||
});
|
||||
|
||||
// Function to calculate the total amount
|
||||
function calTotalAmount() {
|
||||
let subtotal = 0;
|
||||
let vat_amount = 0;
|
||||
|
||||
// Calculate subtotal and VAT from cart list
|
||||
$("#purchase_cart_list tr").each(function () {
|
||||
let cart_qty = getNumericValue($(this).find(".cart-qty").val()) || 0;
|
||||
let cart_price = getNumericValue($(this).find(".price").val()) || 0;
|
||||
let cart_subtotal = cart_qty * cart_price;
|
||||
subtotal += cart_subtotal;
|
||||
|
||||
let cart_vat_rate = parseFloat($(this).data("vat_rate")) || 0;
|
||||
let cart_vat_amount = (cart_subtotal * cart_vat_rate) / 100;
|
||||
vat_amount += cart_vat_amount;
|
||||
|
||||
// Update line total on the UI
|
||||
$(this).find(".cart-subtotal").text(currencyFormat(cart_subtotal));
|
||||
$(this).find(".cart-vat-value").text(currencyFormat(cart_vat_amount));
|
||||
});
|
||||
|
||||
$("#sub_total").text(currencyFormat(subtotal));
|
||||
|
||||
// Display total VAT
|
||||
$("#vat_amount").val(vat_amount.toFixed(2));
|
||||
|
||||
// Subtotal with VAT
|
||||
let subtotal_with_vat = subtotal + vat_amount;
|
||||
|
||||
// Discount
|
||||
let discount_amount = getNumericValue($("#discount_amount").val()) || 0;
|
||||
let discount_type = $(".discount_type").val();
|
||||
|
||||
if (discount_type == "percent") {
|
||||
discount_amount = (subtotal_with_vat * discount_amount) / 100;
|
||||
|
||||
if (discount_amount > subtotal_with_vat) {
|
||||
toastr.error(
|
||||
"Discount cannot be more than 100% of the amount after VAT!"
|
||||
);
|
||||
discount_amount = subtotal_with_vat;
|
||||
$("#discount_amount").val(100);
|
||||
}
|
||||
} else {
|
||||
if (discount_amount > subtotal_with_vat) {
|
||||
toastr.error("Discount cannot be more than the amount after VAT!");
|
||||
discount_amount = subtotal_with_vat;
|
||||
$("#discount_amount").val(discount_amount);
|
||||
}
|
||||
}
|
||||
|
||||
// Shipping Charge
|
||||
let shipping_charge = getNumericValue($("#shipping_charge").val()) || 0;
|
||||
|
||||
// Total Amount (after VAT + shipping, then discount)
|
||||
let total_amount = subtotal_with_vat + shipping_charge - discount_amount;
|
||||
$("#total_amount").text(currencyFormat(total_amount));
|
||||
|
||||
// Receive Amount
|
||||
let receive_amount = getNumericValue($("#receive_amount").val()) || 0;
|
||||
if (receive_amount < 0) {
|
||||
toastr.error("Receive amount cannot be less than 0!");
|
||||
receive_amount = 0;
|
||||
$("#receive_amount").val(receive_amount);
|
||||
}
|
||||
|
||||
// Change Amount
|
||||
let change_amount =
|
||||
receive_amount > total_amount ? receive_amount - total_amount : 0;
|
||||
$("#change_amount").val(change_amount.toFixed(2));
|
||||
|
||||
// Due Amount
|
||||
let due_amount =
|
||||
total_amount > receive_amount ? total_amount - receive_amount : 0;
|
||||
$("#due_amount").val(due_amount.toFixed(2));
|
||||
}
|
||||
calTotalAmount();
|
||||
|
||||
// Cancel btn action
|
||||
$(".cancel-sale-btn").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
clearCart("purchase");
|
||||
});
|
||||
|
||||
$(".product-filter").on("input", ".search-input", function (e) {
|
||||
fetchProducts();
|
||||
});
|
||||
|
||||
let autoOpenedForSearch = false; // Prevent repeated modal open per search
|
||||
function fetchProducts() {
|
||||
var form = $("form.product-filter")[0];
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: $(form).attr("action"),
|
||||
data: new FormData(form),
|
||||
dataType: "json",
|
||||
contentType: false,
|
||||
cache: false,
|
||||
processData: false,
|
||||
success: function (res) {
|
||||
// Show products
|
||||
$("#products-list").html(res.data);
|
||||
|
||||
// Open modal if one product found and not opened yet
|
||||
const searchTerm = $("#purchase_product_search").val().trim();
|
||||
if (
|
||||
res.total_products &&
|
||||
res.product_id &&
|
||||
searchTerm !== "" &&
|
||||
!autoOpenedForSearch
|
||||
) {
|
||||
autoOpenedForSearch = true;
|
||||
autoOpenModal(res.product_id);
|
||||
$("#purchase_product_search").val("");
|
||||
|
||||
// Reset when modal closes and reload list
|
||||
$("#product-modal").one("hidden.bs.modal", function () {
|
||||
autoOpenedForSearch = false;
|
||||
fetchProducts();
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on("click", ".category-content", function () {
|
||||
let url = $("#category-filter").val();
|
||||
const categoryId = $(this).data("id");
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: { category_id: categoryId },
|
||||
success: function (response) {
|
||||
$(".product-list-container").html(response.data);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", ".brand-search", function () {
|
||||
let url = $("#brand-filter").val();
|
||||
const brandId = $(this).data("id");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: { brand_id: brandId },
|
||||
success: function (response) {
|
||||
$(".product-list-container").html(response.data);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Category Filter
|
||||
$(".category-search").on("input", function (e) {
|
||||
e.preventDefault();
|
||||
// Get search query
|
||||
const search = $(this).val();
|
||||
const route = $(this).closest("form").data("route");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: route,
|
||||
data: {
|
||||
search: search,
|
||||
},
|
||||
success: function (response) {
|
||||
$("#category-data").html(response.categories);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// brand Filter
|
||||
$(".brand-search").on("input", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Get search query
|
||||
const search = $(this).val();
|
||||
|
||||
const route = $(this).closest("form").data("route");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: route,
|
||||
data: {
|
||||
search: search,
|
||||
},
|
||||
success: function (response) {
|
||||
$("#brand-data").html(response.brands);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("submit", "#categorySearchForm, #brandSearchForm", function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// When select brand or product
|
||||
$(document).on("click", ".category-list, .brand-list", function () {
|
||||
const isCategory = $(this).hasClass("category-list");
|
||||
const filterType = isCategory ? "category_id" : "brand_id";
|
||||
const filterId = $(this).data("id");
|
||||
const route = $(this).data("route"); // product filter route
|
||||
|
||||
const searchTerm = $("#purchase_product_search").val();
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: route,
|
||||
data: {
|
||||
search: searchTerm,
|
||||
[filterType]: filterId, // Dynamically set category_id or brand_id
|
||||
},
|
||||
success: function (response) {
|
||||
$("#products-list").html(response.data);
|
||||
$("#category-list").html(response.categories);
|
||||
$("#brand-list").html(response.brands);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Update cart
|
||||
$(document).on("change", ".batch_no, .expire_date, .price", function () {
|
||||
let $row = $(this).closest("tr");
|
||||
let updateRoute = $row.data("update_route");
|
||||
|
||||
// Get values
|
||||
let expire_date = $row.find(".expire_date").val();
|
||||
let priceVal = $row.find(".price").val();
|
||||
let batch_no = $row.find(".batch_no").val();
|
||||
|
||||
// Parse and validate price
|
||||
let price = parseFloat(priceVal);
|
||||
if (isNaN(price) || price < 0) {
|
||||
toastr.error("Price cannot be negative or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Optional: clean batch_no
|
||||
if (batch_no === null || batch_no === undefined) {
|
||||
batch_no = "";
|
||||
}
|
||||
|
||||
// Prepare data
|
||||
let data = {
|
||||
batch_no: batch_no,
|
||||
expire_date: expire_date,
|
||||
price: price,
|
||||
};
|
||||
|
||||
// Call update function
|
||||
updateCart(data, updateRoute);
|
||||
});
|
||||
|
||||
// Function to update cart item with the new quantity
|
||||
function updateCart(data, updateRoute) {
|
||||
$.ajax({
|
||||
url: updateRoute,
|
||||
type: "PUT",
|
||||
data: data,
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
fetchUpdatedCart(calTotalAmount); // Refresh the cart and recalculate totals
|
||||
} else {
|
||||
toastr.error(response.message || "Failed to update cart");
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
89
public/assets/js/custom/sale-purchase-return.js
Normal file
89
public/assets/js/custom/sale-purchase-return.js
Normal file
@@ -0,0 +1,89 @@
|
||||
// Get numeric value from string
|
||||
function getNumericValue(value) {
|
||||
return parseFloat(value.replace(/[^0-9.-]+/g, "")) || 0;
|
||||
}
|
||||
|
||||
updateTotalAmount();
|
||||
|
||||
// Update quantity on manual change
|
||||
$(document).on('change', '.return-qty', function() {
|
||||
var row = $(this).closest('tr');
|
||||
var currentQtyInput = $(this);
|
||||
var currentQty = getNumericValue(currentQtyInput.val());
|
||||
var maxQty = parseFloat(row.data('max_qty'));
|
||||
|
||||
// Validate the entered quantity
|
||||
if (currentQty > maxQty) {
|
||||
toastr.error('You cannot return more than the ordered quantity.');
|
||||
currentQtyInput.val(0);
|
||||
} else if (currentQty < 0) {
|
||||
toastr.error('Return quantity cannot be less than 0.');
|
||||
currentQtyInput.val(0);
|
||||
}
|
||||
|
||||
updateSubTotal(row);
|
||||
});
|
||||
|
||||
|
||||
// Increase quantity
|
||||
$(document).on('click', '.add-btn', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Find the closest row and get current quantity and max quantity
|
||||
var row = $(this).closest('tr');
|
||||
var currentQtyInput = row.find('.return-qty');
|
||||
var currentQty = getNumericValue(currentQtyInput.val());
|
||||
var maxQty = parseFloat(row.data('max_qty'));
|
||||
|
||||
// Check if increment will exceed maxQty
|
||||
if ((currentQty + 1) <= maxQty) {
|
||||
currentQtyInput.val(currentQty + 1);
|
||||
updateSubTotal(row);
|
||||
} else {
|
||||
toastr.error('You cannot return more than the ordered quantity.');
|
||||
}
|
||||
});
|
||||
|
||||
// Decrease quantity
|
||||
$(document).on('click', '.sub-btn', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Find the closest row and get current quantity
|
||||
var row = $(this).closest('tr');
|
||||
var currentQtyInput = row.find('.return-qty');
|
||||
var currentQty = getNumericValue(currentQtyInput.val());
|
||||
|
||||
// return quantity cannot negative
|
||||
if ((currentQty - 1) >= 0) {
|
||||
currentQtyInput.val(currentQty - 1);
|
||||
updateSubTotal(row);
|
||||
} else {
|
||||
toastr.error('Return quantity cannot be negative.');
|
||||
}
|
||||
});
|
||||
|
||||
// Update the subtotal for each row based on the return quantity
|
||||
function updateSubTotal(row) {
|
||||
let priceText = row.find(".price").text();
|
||||
let price = getNumericValue(String(priceText || ""))
|
||||
|| getNumericValue(String(row.data("discounted_price_per_unit") || "0"));
|
||||
|
||||
let quantity = getNumericValue(String(row.find(".return-qty").val() || "0"));
|
||||
let subTotal = price * quantity;
|
||||
|
||||
row.find(".subtotal").text(currencyFormat(subTotal));
|
||||
updateTotalAmount();
|
||||
}
|
||||
|
||||
|
||||
// Update total return amount
|
||||
function updateTotalAmount() {
|
||||
var totalReturn = 0;
|
||||
$('.subtotal').each(function() {
|
||||
totalReturn += getNumericValue($(this).text());
|
||||
});
|
||||
|
||||
var returnText = $('.return_amount').data('return-text');
|
||||
|
||||
$('.return_amount').text(returnText + ' ' + currencyFormat(totalReturn));
|
||||
}
|
||||
1129
public/assets/js/custom/sale.js
Normal file
1129
public/assets/js/custom/sale.js
Normal file
File diff suppressed because it is too large
Load Diff
41
public/assets/js/custom/tab.js
Normal file
41
public/assets/js/custom/tab.js
Normal file
@@ -0,0 +1,41 @@
|
||||
function showTab(tabId) {
|
||||
// Activate selected tab
|
||||
document
|
||||
.querySelectorAll(".tab-item")
|
||||
.forEach((tab) => tab.classList.remove("active"));
|
||||
document
|
||||
.querySelectorAll(".tab-content")
|
||||
.forEach((content) => content.classList.remove("active"));
|
||||
|
||||
document.getElementById(tabId).classList.add("active");
|
||||
document
|
||||
.querySelector(`[onclick="showTab('${tabId}')"]`)
|
||||
.classList.add("active");
|
||||
|
||||
// Get the base URL from the hidden input fields
|
||||
const csvBaseUrl = document.getElementById("csvBaseUrl").value;
|
||||
const excelBaseUrl = document.getElementById("excelBaseUrl").value;
|
||||
const pdfBaseUrl = document.getElementById("pdfBaseUrl").value;
|
||||
|
||||
// Set correct export type
|
||||
let type = tabId == "sales" ? "sales" : "purchases";
|
||||
|
||||
// Update export URLs dynamically
|
||||
const csv = document.getElementById("csvExportLink");
|
||||
const excel = document.getElementById("excelExportLink");
|
||||
const pdf = document.getElementById("pdfExportLink");
|
||||
|
||||
if (csv) {
|
||||
csv.href = `${csvBaseUrl}?type=${type}`;
|
||||
}
|
||||
if (excel) {
|
||||
excel.href = `${excelBaseUrl}?type=${type}`;
|
||||
}
|
||||
if (pdf) {
|
||||
pdf.href = `${pdfBaseUrl}?type=${type}`;
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
showTab("sales");
|
||||
});
|
||||
29
public/assets/js/custom/tagify.js
Normal file
29
public/assets/js/custom/tagify.js
Normal file
File diff suppressed because one or more lines are too long
414
public/assets/js/custom/transfer.js
Normal file
414
public/assets/js/custom/transfer.js
Normal file
@@ -0,0 +1,414 @@
|
||||
"use strict";
|
||||
|
||||
let allProducts = [];
|
||||
|
||||
// Asset path helper
|
||||
function assetPath(path) {
|
||||
let baseUrl = $("#asset_base_url").val() || "";
|
||||
return baseUrl.replace(/\/$/, "") + "/" + path.replace(/^\/+/, "");
|
||||
}
|
||||
|
||||
// renders product dropdown list
|
||||
function populateProducts(products) {
|
||||
const $dropdownList = $("#dropdownList");
|
||||
$dropdownList.empty();
|
||||
|
||||
if (products.length === 0) {
|
||||
$dropdownList.append(
|
||||
'<div class="product-option-item">No products available</div>'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const cartRoute = $("#cart-store-url").val();
|
||||
|
||||
$.each(products, function (index, product) {
|
||||
const imageUrl = assetPath(
|
||||
product.productPicture ?? "assets/images/products/box.svg"
|
||||
);
|
||||
let html = "";
|
||||
|
||||
// if multiple stocks
|
||||
if (product.stocks && product.stocks.length > 1) {
|
||||
html += `<div class="product-option-item single-product"
|
||||
data-product_id="${product.id}"
|
||||
data-route="${cartRoute}"
|
||||
data-product_name="${product.productName}"
|
||||
data-product_code="${product.productCode}"
|
||||
data-product_unit_id="${product.unit_id}"
|
||||
data-product_unit_name="${product.unit?.unitName ?? ""}"
|
||||
data-product_image="${product.productPicture}">
|
||||
<div class="product-left">
|
||||
<img src="${imageUrl}" alt="">
|
||||
<div class="product-text">
|
||||
<div class="d-flex align-items-center justify-content-between w-100">
|
||||
<div class="product-title">${
|
||||
product.productName
|
||||
}</div>
|
||||
<p>Code : ${product.productCode}</p>
|
||||
</div>`;
|
||||
|
||||
product.stocks.forEach((stock) => {
|
||||
html += `<div class="d-flex align-items-center justify-content-between w-100 multi-items add-batch-item"
|
||||
data-product_stock_id="${stock.id}"
|
||||
data-product_id="${product.id}"
|
||||
data-product_expire_date="${stock.expire_date ?? ""}"
|
||||
data-product_name="${product.productName}"
|
||||
data-product_code="${product.productCode}"
|
||||
data-default_price="${stock.productSalePrice}"
|
||||
data-product_unit_id="${product.unit_id}"
|
||||
data-product_unit_name="${product.unit?.unitName ?? ""}"
|
||||
data-purchase_price="${stock.productPurchasePrice}"
|
||||
data-product_image="${product.productPicture}"
|
||||
data-route="${cartRoute}"
|
||||
data-batch_no="${stock.batch_no ?? ''}"
|
||||
>
|
||||
|
||||
<div class="product-des">
|
||||
Batch: ${stock.batch_no ?? "N/A"} ${
|
||||
product.color ? ", Color: " + product.color : ""
|
||||
}
|
||||
<span class="product-in-stock">In Stock: ${
|
||||
stock.productStock
|
||||
}</span>
|
||||
</div>
|
||||
<div class="product-price product_price">${currencyFormat(
|
||||
stock.productSalePrice
|
||||
)}</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
html += `</div></div></div>`;
|
||||
} else {
|
||||
const singleStock =
|
||||
Array.isArray(product.stocks) && product.stocks.length > 0
|
||||
? product.stocks[0]
|
||||
: {};
|
||||
|
||||
html += `<div class="product-option-item single-product ${
|
||||
product.id
|
||||
} add-batch-item"
|
||||
data-product_stock_id="${singleStock.id ?? ""}"
|
||||
data-product_id="${product.id}"
|
||||
data-default_price="${singleStock.productSalePrice ?? 0}"
|
||||
data-product_name="${product.productName}"
|
||||
data-product_code="${product.productCode}"
|
||||
data-product_unit_id="${product.unit_id}"
|
||||
data-product_unit_name="${product.unit?.unitName ?? ""}"
|
||||
data-purchase_price="${singleStock.productPurchasePrice ?? 0}"
|
||||
data-product_image="${product.productPicture}"
|
||||
data-product_expire_date="${singleStock.expire_date ?? ""}"
|
||||
data-route="${cartRoute}"
|
||||
data-batch_no="${singleStock.batch_no ?? ''}">
|
||||
|
||||
<div class="product-left">
|
||||
<img src="${imageUrl}" alt="">
|
||||
<div class="product-text">
|
||||
<div class="d-flex align-items-center justify-content-between w-100">
|
||||
<div class="product-title">${
|
||||
product.productName
|
||||
}</div>
|
||||
<p>Code : ${product.productCode}</p>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-between w-100">
|
||||
<div class="product-des">
|
||||
Batch: ${singleStock.batch_no ?? "N/A"} ${
|
||||
product.color ? ", Color: " + product.color : ""
|
||||
}
|
||||
<span class="product-in-stock">In Stock: ${
|
||||
Array.isArray(product.stocks)
|
||||
? product.stocks.reduce(
|
||||
(sum, stock) =>
|
||||
sum + stock.productStock,
|
||||
0
|
||||
)
|
||||
: 0
|
||||
}</span>
|
||||
</div>
|
||||
<div class="product-price product_price">${currencyFormat(
|
||||
singleStock.productSalePrice ?? 0
|
||||
)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
$dropdownList.append(html);
|
||||
});
|
||||
}
|
||||
|
||||
// load all products initially
|
||||
const allProductsUrl = $("#all-products").val();
|
||||
if (allProductsUrl) {
|
||||
$.get(allProductsUrl, function (products) {
|
||||
allProducts = products; // store globally
|
||||
populateProducts(allProducts);
|
||||
});
|
||||
}
|
||||
|
||||
// Filter products based on branch & warehouse
|
||||
function filterProducts() {
|
||||
const branchId = $("#from_branch").val();
|
||||
const warehouseId = $("#from_warehouse").val();
|
||||
|
||||
const filtered = allProducts
|
||||
.map((product) => {
|
||||
if (!product.stocks || product.stocks.length === 0) return null;
|
||||
|
||||
// filter stocks by selected branch & warehouse
|
||||
const filteredStocks = product.stocks.filter((stock) => {
|
||||
const branchMatch = !branchId || stock.branch_id == branchId;
|
||||
const warehouseMatch = !warehouseId || stock.warehouse_id == warehouseId;
|
||||
return branchMatch && warehouseMatch && stock.productStock > 0;
|
||||
});
|
||||
|
||||
if (filteredStocks.length === 0) return null;
|
||||
|
||||
return {
|
||||
...product, // copy product data, keep only filtered stocks
|
||||
stocks: filteredStocks,
|
||||
};
|
||||
})
|
||||
.filter(Boolean); // remove nulls
|
||||
|
||||
populateProducts(filtered);
|
||||
}
|
||||
|
||||
// toggle product dropdown visibility
|
||||
const $dropdown = $("#dropdownList");
|
||||
const $searchContainer = $("#searchContainer");
|
||||
const $productSearch = $("#productSearch");
|
||||
const $arrow = $("#arrow");
|
||||
|
||||
$("#productDropdown .product-selected").on("click", function (e) {
|
||||
e.stopPropagation();
|
||||
$dropdown.toggle();
|
||||
$searchContainer.toggleClass("hidden");
|
||||
$arrow.toggleClass("product-rotate");
|
||||
});
|
||||
|
||||
// Close dropdown if clicked outside
|
||||
$(document).on("click", function (e) {
|
||||
if (!$(e.target).closest("#productDropdown").length) {
|
||||
$dropdown.hide();
|
||||
$searchContainer.addClass("hidden");
|
||||
$arrow.removeClass("product-rotate");
|
||||
}
|
||||
});
|
||||
|
||||
// product search also respects filter
|
||||
$productSearch.on("keyup", function () {
|
||||
const searchTerm = $(this).val().toLowerCase();
|
||||
$("#dropdownList .product-option-item").each(function () {
|
||||
const name = String($(this).data("product_name") || "").toLowerCase();
|
||||
const code = String($(this).data("product_code") || "").toLowerCase();
|
||||
$(this).toggle(name.includes(searchTerm) || code.includes(searchTerm));
|
||||
});
|
||||
});
|
||||
|
||||
// Add to cart on batch click
|
||||
$("#dropdownList").on("click", ".add-batch-item", function (e) {
|
||||
e.stopPropagation();
|
||||
|
||||
const $item = $(this);
|
||||
$dropdown.hide();
|
||||
$searchContainer.addClass("hidden");
|
||||
$arrow.removeClass("product-rotate");
|
||||
|
||||
let stockId = $item.data("product_stock_id");
|
||||
let productId = $item.data("product_id");
|
||||
|
||||
let name = $item.data("product_name");
|
||||
let code = $item.data("product_code");
|
||||
let price = parseFloat($item.data("default_price") || 0);
|
||||
let batchNo = $item.data("batch_no") || "";
|
||||
let image = $item.data("product_image") || assetPath("assets/images/products/box.svg");
|
||||
|
||||
// If row exists increment qty
|
||||
let existingRow = $("#product-row-" + stockId);
|
||||
if (existingRow.length) {
|
||||
let qtyInput = existingRow.find(".dynamic-qty");
|
||||
qtyInput.val((parseInt(qtyInput.val()) || 1) + 1).trigger("input");
|
||||
return;
|
||||
}
|
||||
|
||||
let rowHtml = `
|
||||
<tr id="product-row-${stockId}">
|
||||
<td><img src="${assetPath(image)}" width="40"></td>
|
||||
<td>${name}</td>
|
||||
<td>${code}</td>
|
||||
<td>${batchNo}</td>
|
||||
<td class="text-start">
|
||||
<div class="d-flex align-items-center justify-content-center">
|
||||
<button type="button" class="incre-decre subtract-btn btn btn-sm btn-outline-secondary"><i class="fas fa-minus"></i></button>
|
||||
<input type="number" name="products[${stockId}][quantity]" class="custom-number-input dynamic-qty form-control form-control-sm mx-1 text-center" value="1" min="1" step="1" style="width: 60px;">
|
||||
<button type="button" class="incre-decre adding-btn btn btn-sm btn-outline-secondary"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="number" name="products[${stockId}][unit_price]" class="form-control unit-price text-center" value="${price}" min="0">
|
||||
</td>
|
||||
<td><input type="number" name="products[${stockId}][tax]" class="form-control tax text-center" value="0" min="0"></td>
|
||||
<td><input type="number" name="products[${stockId}][discount]" class="form-control discount text-center" value="0" min="0"></td>
|
||||
<td class="sub-total text-center">${price.toFixed(2)}</td>
|
||||
<td>
|
||||
<button type="button" class="x-btn remove-btn" data-id="${stockId}">
|
||||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none">
|
||||
<path d="M18.5 6L6.5 18" stroke="#E13F3D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.5 6L18.5 18" stroke="#E13F3D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
<input type="hidden" name="products[${stockId}][product_id]" value="${productId}">
|
||||
</tr>
|
||||
`;
|
||||
$("#product-list").append(rowHtml);
|
||||
updateTotals();
|
||||
});
|
||||
|
||||
// Cart qty, price, discount, tax change
|
||||
$("#product-list").on(
|
||||
"input",
|
||||
".dynamic-qty, .unit-price, .discount, .tax",
|
||||
function () {
|
||||
let $row = $(this).closest("tr");
|
||||
let qty = parseFloat($row.find(".dynamic-qty").val()) || 0;
|
||||
let price = parseFloat($row.find(".unit-price").val()) || 0;
|
||||
let discount = parseFloat($row.find(".discount").val()) || 0;
|
||||
let tax = parseFloat($row.find(".tax").val()) || 0;
|
||||
|
||||
let subtotal = qty * price + tax - discount;
|
||||
$row.find(".sub-total").text(subtotal.toFixed(2));
|
||||
updateTotals();
|
||||
}
|
||||
);
|
||||
|
||||
// increment / decrement buttons
|
||||
$("#product-list").on("click", ".adding-btn", function () {
|
||||
let $row = $(this).closest("tr");
|
||||
let $qty = $row.find(".dynamic-qty");
|
||||
$qty.val((parseInt($qty.val()) || 0) + 1).trigger("input");
|
||||
});
|
||||
$("#product-list").on("click", ".subtract-btn", function () {
|
||||
let $row = $(this).closest("tr");
|
||||
let $qty = $row.find(".dynamic-qty");
|
||||
let current = parseInt($qty.val()) || 1;
|
||||
if (current > 1) $qty.val(current - 1).trigger("input");
|
||||
});
|
||||
|
||||
// remove row
|
||||
$("#product-list").on("click", ".remove-btn", function () {
|
||||
$(this).closest("tr").remove();
|
||||
updateTotals();
|
||||
});
|
||||
|
||||
// update totals function
|
||||
function updateTotals() {
|
||||
let subTotal = 0,
|
||||
totalDiscount = 0,
|
||||
totalTax = 0,
|
||||
grandTotal = 0;
|
||||
|
||||
$("#product-list tr").each(function () {
|
||||
let qty = parseFloat($(this).find(".dynamic-qty").val()) || 0;
|
||||
let price = parseFloat($(this).find(".unit-price").val()) || 0;
|
||||
let discount = parseFloat($(this).find(".discount").val()) || 0;
|
||||
let tax = parseFloat($(this).find(".tax").val()) || 0;
|
||||
|
||||
let lineBase = qty * price;
|
||||
let lineWithTax = lineBase + tax;
|
||||
let lineSubtotal = lineWithTax - discount;
|
||||
|
||||
$(this).find(".sub-total").text(lineSubtotal.toFixed(2));
|
||||
|
||||
subTotal += lineBase;
|
||||
totalTax += tax;
|
||||
totalDiscount += discount;
|
||||
grandTotal += lineSubtotal;
|
||||
});
|
||||
|
||||
let shipping = parseFloat($("#shipping_amount").val()) || 0;
|
||||
grandTotal += shipping;
|
||||
|
||||
$("#total_amount").text(subTotal.toFixed(2));
|
||||
$("#tax_amount").text(totalTax.toFixed(2));
|
||||
$("#discount_amount").text(totalDiscount.toFixed(2));
|
||||
$("#grand_total_amount").text(grandTotal.toFixed(2));
|
||||
}
|
||||
|
||||
// Trigger totals update on shipping change
|
||||
$("#shipping_amount").on("input", function () {
|
||||
updateTotals();
|
||||
});
|
||||
|
||||
const allWarehousesUrl = $("#branch_wise_warehouses").val();
|
||||
|
||||
function loadWarehouses(branchId, warehouseSelect) {
|
||||
if (!allWarehousesUrl) return;
|
||||
|
||||
$.ajax({
|
||||
url: allWarehousesUrl,
|
||||
type: "GET",
|
||||
data: { branch_id: branchId },
|
||||
success: function (warehouses) {
|
||||
let options = '<option value="">Select one</option>';
|
||||
|
||||
if (warehouses.length > 0) {
|
||||
$.each(warehouses, function (i, wh) {
|
||||
options += `<option value="${wh.id}">${wh.name}</option>`;
|
||||
});
|
||||
} else {
|
||||
options += `<option value="">No warehouse available</option>`;
|
||||
}
|
||||
|
||||
$(warehouseSelect).html(options);
|
||||
},
|
||||
error: function () {
|
||||
$(warehouseSelect).html('<option value="">Error loading warehouses</option>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load all warehouses initially
|
||||
loadWarehouses("", "#from_warehouse");
|
||||
loadWarehouses("", "#to_warehouse");
|
||||
|
||||
// When "from_branch" changes
|
||||
$("#from_branch").on("change", function () {
|
||||
let branchId = $(this).val();
|
||||
|
||||
// Clear cart and update totals
|
||||
$("#product-list").empty();
|
||||
updateTotals();
|
||||
|
||||
// Reload "from_warehouse" options based on selected branch
|
||||
loadWarehouses(branchId, "#from_warehouse");
|
||||
|
||||
// Filter products based on new branch & warehouse
|
||||
filterProducts();
|
||||
});
|
||||
|
||||
// When "to_branch" changes
|
||||
$("#to_branch").on("change", function () {
|
||||
let branchId = $(this).val();
|
||||
let hasActiveBranch = $("#hasActiveBranch").val() == "1";
|
||||
|
||||
// Only reload "to_warehouse" if no active branch
|
||||
if (!hasActiveBranch) {
|
||||
loadWarehouses(branchId, "#to_warehouse");
|
||||
}
|
||||
});
|
||||
|
||||
// When "from_warehouse" changes
|
||||
$("#from_warehouse").on("change", function () {
|
||||
// Clear cart and update totals
|
||||
$("#product-list").empty();
|
||||
updateTotals();
|
||||
|
||||
// Filter products based on new warehouse
|
||||
filterProducts();
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user