migrate to gtea from bistbucket

This commit is contained in:
2026-03-15 17:08:23 +07:00
commit 129ca2260c
3716 changed files with 566316 additions and 0 deletions

View 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

View 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);
});
});

View 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";
});
});

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View 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");
}
},
});
}

View 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));
}

File diff suppressed because it is too large Load Diff

View 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");
});

File diff suppressed because one or more lines are too long

View 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();
});