update
This commit is contained in:
BIN
fusion_loaners_management/static/description/icon.png
Normal file
BIN
fusion_loaners_management/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
443
fusion_loaners_management/static/src/js/loaner_portal.js
Normal file
443
fusion_loaners_management/static/src/js/loaner_portal.js
Normal file
@@ -0,0 +1,443 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import publicWidget from "@web/legacy/js/public/public_widget";
|
||||
|
||||
publicWidget.registry.LoanerPortal = publicWidget.Widget.extend({
|
||||
selector: '#loanerSection, #btn_checkout_loaner, .btn-loaner-return',
|
||||
|
||||
start: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._allProducts = [];
|
||||
this._initLoanerSection();
|
||||
this._initCheckoutButton();
|
||||
this._initReturnButtons();
|
||||
this._initModal();
|
||||
},
|
||||
|
||||
_initModal: function () {
|
||||
var self = this;
|
||||
var modal = document.getElementById('loanerCheckoutModal');
|
||||
if (!modal) return;
|
||||
|
||||
var categorySelect = document.getElementById('modal_category_id');
|
||||
var productSelect = document.getElementById('modal_product_id');
|
||||
var lotSelect = document.getElementById('modal_lot_id');
|
||||
var loanDays = document.getElementById('modal_loan_days');
|
||||
var btnCheckout = document.getElementById('modal_btn_checkout');
|
||||
var btnCreateProduct = document.getElementById('modal_btn_create_product');
|
||||
var newCategorySelect = document.getElementById('modal_new_category_id');
|
||||
var createResult = document.getElementById('modal_create_result');
|
||||
|
||||
modal.addEventListener('show.bs.modal', function () {
|
||||
self._loadCategories(categorySelect, newCategorySelect);
|
||||
self._loadProducts(null, productSelect, lotSelect);
|
||||
});
|
||||
|
||||
if (categorySelect) {
|
||||
categorySelect.addEventListener('change', function () {
|
||||
var catId = this.value ? parseInt(this.value) : null;
|
||||
self._filterProducts(catId, productSelect, lotSelect);
|
||||
});
|
||||
}
|
||||
|
||||
if (productSelect) {
|
||||
productSelect.addEventListener('change', function () {
|
||||
var prodId = this.value ? parseInt(this.value) : null;
|
||||
self._filterLots(prodId, lotSelect, loanDays);
|
||||
});
|
||||
}
|
||||
|
||||
if (btnCreateProduct) {
|
||||
btnCreateProduct.addEventListener('click', function () {
|
||||
var name = document.getElementById('modal_new_product_name').value.trim();
|
||||
var serial = document.getElementById('modal_new_serial').value.trim();
|
||||
var catId = newCategorySelect ? newCategorySelect.value : '';
|
||||
|
||||
if (!name || !serial) {
|
||||
alert('Please enter both product name and serial number.');
|
||||
return;
|
||||
}
|
||||
|
||||
btnCreateProduct.disabled = true;
|
||||
btnCreateProduct.innerHTML = '<i class="fa fa-spinner fa-spin me-1"></i> Creating...';
|
||||
|
||||
self._rpc('/my/loaner/create-product', {
|
||||
product_name: name,
|
||||
serial_number: serial,
|
||||
category_id: catId || null,
|
||||
}).then(function (result) {
|
||||
if (result.success) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = result.product_id;
|
||||
opt.text = result.product_name;
|
||||
opt.selected = true;
|
||||
productSelect.appendChild(opt);
|
||||
|
||||
lotSelect.innerHTML = '';
|
||||
var lotOpt = document.createElement('option');
|
||||
lotOpt.value = result.lot_id;
|
||||
lotOpt.text = result.lot_name;
|
||||
lotOpt.selected = true;
|
||||
lotSelect.appendChild(lotOpt);
|
||||
|
||||
self._allProducts.push({
|
||||
id: result.product_id,
|
||||
name: result.product_name,
|
||||
category_id: catId ? parseInt(catId) : null,
|
||||
period_days: 7,
|
||||
lots: [{ id: result.lot_id, name: result.lot_name }],
|
||||
});
|
||||
|
||||
if (createResult) {
|
||||
createResult.style.display = '';
|
||||
createResult.innerHTML = '<div class="alert alert-success py-2"><i class="fa fa-check me-1"></i> "' + result.product_name + '" (S/N: ' + result.lot_name + ') created!</div>';
|
||||
}
|
||||
|
||||
document.getElementById('modal_new_product_name').value = '';
|
||||
document.getElementById('modal_new_serial').value = '';
|
||||
} else {
|
||||
if (createResult) {
|
||||
createResult.style.display = '';
|
||||
createResult.innerHTML = '<div class="alert alert-danger py-2">' + (result.error || 'Error') + '</div>';
|
||||
}
|
||||
}
|
||||
btnCreateProduct.disabled = false;
|
||||
btnCreateProduct.innerHTML = '<i class="fa fa-plus me-1"></i> Create Product';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (btnCheckout) {
|
||||
btnCheckout.addEventListener('click', function () {
|
||||
var productId = productSelect.value ? parseInt(productSelect.value) : null;
|
||||
var lotId = lotSelect.value ? parseInt(lotSelect.value) : null;
|
||||
var days = parseInt(loanDays.value) || 7;
|
||||
var orderId = document.getElementById('modal_order_id').value;
|
||||
var clientId = document.getElementById('modal_client_id').value;
|
||||
|
||||
if (!productId) {
|
||||
alert('Please select a product.');
|
||||
return;
|
||||
}
|
||||
|
||||
btnCheckout.disabled = true;
|
||||
btnCheckout.innerHTML = '<i class="fa fa-spinner fa-spin me-1"></i> Processing...';
|
||||
|
||||
self._rpc('/my/loaner/checkout', {
|
||||
product_id: productId,
|
||||
lot_id: lotId,
|
||||
sale_order_id: orderId ? parseInt(orderId) : null,
|
||||
client_id: clientId ? parseInt(clientId) : null,
|
||||
loaner_period_days: days,
|
||||
checkout_condition: 'good',
|
||||
checkout_notes: '',
|
||||
}).then(function (result) {
|
||||
if (result.success) {
|
||||
self._hideModal(modal);
|
||||
alert(result.message);
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error: ' + (result.error || 'Unknown'));
|
||||
btnCheckout.disabled = false;
|
||||
btnCheckout.innerHTML = '<i class="fa fa-check me-1"></i> Checkout Loaner';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_loadCategories: function (categorySelect, newCategorySelect) {
|
||||
this._rpc('/my/loaner/categories', {}).then(function (categories) {
|
||||
categories = categories || [];
|
||||
if (categorySelect) {
|
||||
categorySelect.innerHTML = '<option value="">All Categories</option>';
|
||||
categories.forEach(function (c) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = c.id;
|
||||
opt.text = c.name;
|
||||
categorySelect.appendChild(opt);
|
||||
});
|
||||
}
|
||||
if (newCategorySelect) {
|
||||
newCategorySelect.innerHTML = '<option value="">-- Select --</option>';
|
||||
categories.forEach(function (c) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = c.id;
|
||||
opt.text = c.name;
|
||||
newCategorySelect.appendChild(opt);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_loadProducts: function (categoryId, productSelect, lotSelect) {
|
||||
var self = this;
|
||||
var params = {};
|
||||
if (categoryId) params.category_id = categoryId;
|
||||
|
||||
this._rpc('/my/loaner/products', params).then(function (products) {
|
||||
self._allProducts = products || [];
|
||||
self._renderProducts(self._allProducts, productSelect);
|
||||
if (lotSelect) lotSelect.innerHTML = '<option value="">-- Select Serial --</option>';
|
||||
});
|
||||
},
|
||||
|
||||
_filterProducts: function (categoryId, productSelect, lotSelect) {
|
||||
var filtered = this._allProducts;
|
||||
if (categoryId) {
|
||||
filtered = this._allProducts.filter(function (p) { return p.category_id === categoryId; });
|
||||
}
|
||||
this._renderProducts(filtered, productSelect);
|
||||
if (lotSelect) lotSelect.innerHTML = '<option value="">-- Select Serial --</option>';
|
||||
},
|
||||
|
||||
_renderProducts: function (products, productSelect) {
|
||||
if (!productSelect) return;
|
||||
productSelect.innerHTML = '<option value="">-- Select Product --</option>';
|
||||
products.forEach(function (p) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = p.id;
|
||||
opt.text = p.name + ' (' + p.lots.length + ' avail)';
|
||||
productSelect.appendChild(opt);
|
||||
});
|
||||
},
|
||||
|
||||
_filterLots: function (productId, lotSelect, loanDays) {
|
||||
if (!lotSelect) return;
|
||||
lotSelect.innerHTML = '<option value="">-- Select Serial --</option>';
|
||||
if (!productId) return;
|
||||
var product = this._allProducts.find(function (p) { return p.id === productId; });
|
||||
if (product) {
|
||||
product.lots.forEach(function (lot) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = lot.id;
|
||||
opt.text = lot.name;
|
||||
lotSelect.appendChild(opt);
|
||||
});
|
||||
if (loanDays && product.period_days) {
|
||||
loanDays.value = product.period_days;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_initCheckoutButton: function () {
|
||||
var self = this;
|
||||
var btns = document.querySelectorAll('#btn_checkout_loaner');
|
||||
btns.forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var orderId = btn.dataset.orderId || '';
|
||||
var clientId = btn.dataset.clientId || '';
|
||||
var modalOrderId = document.getElementById('modal_order_id');
|
||||
var modalClientId = document.getElementById('modal_client_id');
|
||||
if (modalOrderId) modalOrderId.value = orderId;
|
||||
if (modalClientId) modalClientId.value = clientId;
|
||||
var modal = document.getElementById('loanerCheckoutModal');
|
||||
self._showModal(modal);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_initReturnButtons: function () {
|
||||
var self = this;
|
||||
var returnModal = document.getElementById('loanerReturnModal');
|
||||
if (!returnModal) return;
|
||||
|
||||
var btnSubmitReturn = document.getElementById('return_modal_btn_submit');
|
||||
|
||||
document.querySelectorAll('.btn-loaner-return').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var checkoutId = parseInt(btn.dataset.checkoutId);
|
||||
var productName = btn.dataset.productName || 'Loaner';
|
||||
|
||||
document.getElementById('return_modal_checkout_id').value = checkoutId;
|
||||
document.getElementById('return_modal_product_name').textContent = productName;
|
||||
document.getElementById('return_modal_condition').value = 'good';
|
||||
document.getElementById('return_modal_notes').value = '';
|
||||
|
||||
var locSelect = document.getElementById('return_modal_location_id');
|
||||
locSelect.innerHTML = '<option value="">-- Loading... --</option>';
|
||||
self._rpc('/my/loaner/locations', {}).then(function (locations) {
|
||||
locations = locations || [];
|
||||
locSelect.innerHTML = '<option value="">-- Select Location --</option>';
|
||||
locations.forEach(function (l) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = l.id;
|
||||
opt.text = l.name;
|
||||
locSelect.appendChild(opt);
|
||||
});
|
||||
});
|
||||
self._showModal(returnModal);
|
||||
});
|
||||
});
|
||||
|
||||
if (btnSubmitReturn) {
|
||||
btnSubmitReturn.addEventListener('click', function () {
|
||||
var checkoutId = parseInt(document.getElementById('return_modal_checkout_id').value);
|
||||
var condition = document.getElementById('return_modal_condition').value;
|
||||
var notes = document.getElementById('return_modal_notes').value;
|
||||
var locationId = document.getElementById('return_modal_location_id').value;
|
||||
|
||||
btnSubmitReturn.disabled = true;
|
||||
btnSubmitReturn.innerHTML = '<i class="fa fa-spinner fa-spin me-1"></i> Processing...';
|
||||
|
||||
self._rpc('/my/loaner/return', {
|
||||
checkout_id: checkoutId,
|
||||
return_condition: condition,
|
||||
return_notes: notes,
|
||||
return_location_id: locationId ? parseInt(locationId) : null,
|
||||
}).then(function (result) {
|
||||
if (result.success) {
|
||||
self._hideModal(returnModal);
|
||||
alert(result.message);
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error: ' + (result.error || 'Unknown'));
|
||||
btnSubmitReturn.disabled = false;
|
||||
btnSubmitReturn.innerHTML = '<i class="fa fa-check me-1"></i> Confirm Return';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_initLoanerSection: function () {
|
||||
var self = this;
|
||||
var loanerSection = document.getElementById('loanerSection');
|
||||
if (!loanerSection) return;
|
||||
|
||||
var productSelect = document.getElementById('loaner_product_id');
|
||||
var lotSelect = document.getElementById('loaner_lot_id');
|
||||
var periodInput = document.getElementById('loaner_period_days');
|
||||
var checkoutFlag = document.getElementById('loaner_checkout');
|
||||
var existingFields = document.getElementById('loaner_existing_fields');
|
||||
var newFields = document.getElementById('loaner_new_fields');
|
||||
var modeRadios = document.querySelectorAll('input[name="loaner_mode"]');
|
||||
var btnCreate = document.getElementById('btn_create_loaner_product');
|
||||
var createResult = document.getElementById('loaner_create_result');
|
||||
var productsData = [];
|
||||
|
||||
loanerSection.addEventListener('show.bs.collapse', function () {
|
||||
if (productSelect && productSelect.options.length <= 1) {
|
||||
self._rpc('/my/loaner/products', {}).then(function (data) {
|
||||
productsData = data || [];
|
||||
productSelect.innerHTML = '<option value="">-- Select Product --</option>';
|
||||
productsData.forEach(function (p) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = p.id;
|
||||
opt.text = p.name + ' (' + p.lots.length + ' avail)';
|
||||
productSelect.appendChild(opt);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
loanerSection.addEventListener('shown.bs.collapse', function () {
|
||||
if (checkoutFlag) checkoutFlag.value = '1';
|
||||
});
|
||||
loanerSection.addEventListener('hidden.bs.collapse', function () {
|
||||
if (checkoutFlag) checkoutFlag.value = '0';
|
||||
});
|
||||
|
||||
modeRadios.forEach(function (radio) {
|
||||
radio.addEventListener('change', function () {
|
||||
if (this.value === 'existing') {
|
||||
if (existingFields) existingFields.style.display = '';
|
||||
if (newFields) newFields.style.display = 'none';
|
||||
} else {
|
||||
if (existingFields) existingFields.style.display = 'none';
|
||||
if (newFields) newFields.style.display = '';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (productSelect) {
|
||||
productSelect.addEventListener('change', function () {
|
||||
lotSelect.innerHTML = '<option value="">-- Select Serial --</option>';
|
||||
var product = productsData.find(function (p) { return p.id === parseInt(productSelect.value); });
|
||||
if (product) {
|
||||
product.lots.forEach(function (lot) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = lot.id;
|
||||
opt.text = lot.name;
|
||||
lotSelect.appendChild(opt);
|
||||
});
|
||||
if (periodInput && product.period_days) periodInput.value = product.period_days;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (btnCreate) {
|
||||
btnCreate.addEventListener('click', function () {
|
||||
var name = document.getElementById('loaner_new_product_name').value.trim();
|
||||
var serial = document.getElementById('loaner_new_serial').value.trim();
|
||||
if (!name || !serial) { alert('Enter both name and serial.'); return; }
|
||||
btnCreate.disabled = true;
|
||||
btnCreate.innerHTML = '<i class="fa fa-spinner fa-spin me-1"></i> Creating...';
|
||||
|
||||
self._rpc('/my/loaner/create-product', {
|
||||
product_name: name, serial_number: serial,
|
||||
}).then(function (result) {
|
||||
if (result.success) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = result.product_id;
|
||||
opt.text = result.product_name;
|
||||
opt.selected = true;
|
||||
productSelect.appendChild(opt);
|
||||
lotSelect.innerHTML = '';
|
||||
var lotOpt = document.createElement('option');
|
||||
lotOpt.value = result.lot_id;
|
||||
lotOpt.text = result.lot_name;
|
||||
lotOpt.selected = true;
|
||||
lotSelect.appendChild(lotOpt);
|
||||
document.getElementById('loaner_existing').checked = true;
|
||||
if (existingFields) existingFields.style.display = '';
|
||||
if (newFields) newFields.style.display = 'none';
|
||||
if (createResult) {
|
||||
createResult.style.display = '';
|
||||
createResult.innerHTML = '<div class="alert alert-success py-2">Created "' + result.product_name + '" (S/N: ' + result.lot_name + ')</div>';
|
||||
}
|
||||
} else {
|
||||
if (createResult) {
|
||||
createResult.style.display = '';
|
||||
createResult.innerHTML = '<div class="alert alert-danger py-2">' + (result.error || 'Error') + '</div>';
|
||||
}
|
||||
}
|
||||
btnCreate.disabled = false;
|
||||
btnCreate.innerHTML = '<i class="fa fa-plus me-1"></i> Create Product';
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_rpc: function (url, params) {
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ jsonrpc: '2.0', method: 'call', id: 1, params: params }),
|
||||
}).then(function (r) { return r.json(); }).then(function (d) { return d.result; });
|
||||
},
|
||||
|
||||
_showModal: function (modalEl) {
|
||||
if (!modalEl) return;
|
||||
var Modal = window.bootstrap ? window.bootstrap.Modal : null;
|
||||
if (Modal) {
|
||||
var inst = Modal.getOrCreateInstance ? Modal.getOrCreateInstance(modalEl) : new Modal(modalEl);
|
||||
inst.show();
|
||||
} else if (window.$ || window.jQuery) {
|
||||
(window.$ || window.jQuery)(modalEl).modal('show');
|
||||
}
|
||||
},
|
||||
|
||||
_hideModal: function (modalEl) {
|
||||
if (!modalEl) return;
|
||||
try {
|
||||
var Modal = window.bootstrap ? window.bootstrap.Modal : null;
|
||||
if (Modal && Modal.getInstance) {
|
||||
var inst = Modal.getInstance(modalEl);
|
||||
if (inst) inst.hide();
|
||||
} else if (window.$ || window.jQuery) {
|
||||
(window.$ || window.jQuery)(modalEl).modal('hide');
|
||||
}
|
||||
} catch (e) { /* non-blocking */ }
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user