Rev 36318 | Blame | Compare with Previous | Last modification | View Log | RSS feed
<style>.cpr-form {max-width: 960px;}.cpr-form label {display: block;margin-top: 10px;font-weight: 600;font-size: 13px;}.cpr-form input[type=text], .cpr-form input[type=number], .cpr-form select, .cpr-form textarea {width: 100%;padding: 6px;font-size: 13px;box-sizing: border-box;}.cpr-form textarea {min-height: 60px;font-family: monospace;}.cpr-form .inline-row {display: flex;gap: 8px;margin-top: 4px;}.cpr-form .inline-row input {flex: 1;}.cpr-form .inline-row .remove-row {flex: 0 0 auto;padding: 4px 10px;}.cpr-form .actions {margin-top: 16px;}.cpr-notice {background: #fcf8e3;border: 1px solid #faebcc;color: #8a6d3b;padding: 8px 12px;border-radius: 3px;font-size: 12px;margin-bottom: 12px;}.cpr-panel {border: 1px solid #ddd;padding: 10px;border-radius: 3px;margin-top: 12px;background: #fafafa;}.cpr-panel h4 {margin: 0 0 8px 0;font-size: 14px;}.cpr-add-btn {margin-top: 6px;}.cpr-vendor-wrap {position: relative;}.cpr-vendor-search {margin-bottom: 0;}.cpr-vendor-list {position: absolute;z-index: 10;width: 100%;max-height: 260px;overflow-y: auto;background: #fff;border: 1px solid #ccc;border-top: none;display: none;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);}.cpr-vendor-list .item {padding: 6px 10px;cursor: pointer;font-size: 13px;}.cpr-vendor-list .item:hover, .cpr-vendor-list .item.active {background: #f0f0f0;}.cpr-vendor-selected {margin-top: 4px;padding: 6px 10px;background: #eafaf1;border: 1px solid #b7e0c7;border-radius: 3px;font-size: 13px;}.cpr-vendor-selected .clear-vendor {float: right;cursor: pointer;color: #888;}.cpr-tabs {display: flex;gap: 4px;margin-top: 12px;border-bottom: 1px solid #ddd;}.cpr-tabs button {background: #eee;border: 1px solid #ccc;border-bottom: none;padding: 6px 12px;cursor: pointer;font-size: 13px;}.cpr-tabs button.active {background: #fff;font-weight: 600;}.cpr-tab-body {display: none;border: 1px solid #ddd;border-top: none;padding: 10px;background: #fff;}.cpr-tab-body.active {display: block;}#bulk-preview table {width: 100%;font-size: 12px;border-collapse: collapse;margin-top: 6px;}#bulk-preview th, #bulk-preview td {border: 1px solid #ddd;padding: 4px 6px;}#bulk-preview th {background: #f0f0f0;text-align: left;}#bulk-invalid {color: #a94442;font-size: 12px;margin-top: 6px;}.bulk-grand {font-weight: 700;margin-top: 6px;font-size: 13px;}</style><section class="wrapper"><div class="row"><div class="col-lg-12"><h3 class="page-header"><i class="icon_document_alt"></i>Create Purchase Return</h3><ol class="breadcrumb"><li><i class="fa fa-home"></i><a href="${rc.contextPath}/dashboard">Home</a></li><li><a href="javascript:void(0);" class="unsettled-purchase-returns">Unsettled Purchase Returns</a></li><li><i class="icon_document_alt"></i>Create</li></ol></div></div><div class="row"><div class="col-lg-12 cpr-form">## <div class="cpr-notice">## Amount is computed from <code>lineitem.unitPrice</code>. Bad Purchase Return is not yet supported in## fofo — use the legacy app for BAD.## </div><label for="cpr-bwh">Billing Warehouse Location</label><select id="cpr-bwh" name="billingWarehouseId" required><option value="">-- Select Warehouse --</option>#foreach($entry in $warehouseMap.entrySet())<option value="$entry.getKey()">$entry.getKey() — $entry.getValue()</option>#end</select><label for="cpr-reason-type">Purchase Return Reason Type</label><select id="cpr-reason-type" name="reasonType"><option value="ACTUAL_PR" selected="selected">ACTUAL_PR</option><option value="REPLACEMENT">REPLACEMENT</option></select><label for="cpr-reason-text">Reason Text</label><textarea id="cpr-reason-text" name="reasonText" maxlength="1024" placeholder="Enter the reason"></textarea><div class="cpr-tabs"><button type="button" class="cpr-tab active" data-tab="bulk">Bulk IMEI (auto-split by invoice)</button><button type="button" class="cpr-tab" data-tab="manual">Single PR (manual entry)</button></div><!-- ========= BULK IMEI TAB ========= --><div class="cpr-tab-body active" id="tab-bulk"><label>Paste comma-separated IMEIs (also accepts newlines)</label><textarea id="bulk-imei-text" rows="5"placeholder="863477088239385, 860552089650534, 864001081655170"></textarea><label>Or upload a file (.txt / .csv — comma or newline separated)</label><input type="file" id="bulk-imei-file"/><div class="actions"><button type="button" id="bulk-verify" class="btn btn-default">Verify</button><button type="button" id="bulk-create" class="btn btn-primary" disabled>Create Returns & DebitNotes</button></div><div id="bulk-preview" style="margin-top:10px;"></div><div id="bulk-invalid"></div></div><!-- ========= MANUAL TAB ========= --><div class="cpr-tab-body" id="tab-manual"><form id="create-pr-form" enctype="multipart/form-data" onsubmit="return false;"><label for="cpr-vendor-search">Vendor</label><div class="cpr-vendor-wrap"><input type="text" id="cpr-vendor-search" class="cpr-vendor-search" autocomplete="off"placeholder="Type vendor name to search..."/><div class="cpr-vendor-list" id="cpr-vendor-list">#foreach($s in $suppliers)<div class="item" data-id="$s.getId()" data-name="$s.getName()">$s.getName()(id=$s.getId())</div>#end</div><input type="hidden" id="cpr-vendor" name="vendorId" required/><div class="cpr-vendor-selected" id="cpr-vendor-selected" style="display:none;"><span class="clear-vendor" title="Clear">×</span><span class="selected-label"></span></div></div><label style="margin-top: 14px;"><input type="checkbox" id="cpr-bad" name="badPurchaseReturn" value="on"/> Bad Purchase Return</label><div class="cpr-panel"><h4>Serialized Items (IMEI)</h4><div id="imei-container"><div class="inline-row"><input type="text" name="returnImeiNumber" class="imei-number"placeholder="IMEI Number"/><button type="button" class="btn btn-xs btn-default remove-row">×</button></div></div><button type="button" id="add-imei" class="btn btn-xs btn-default cpr-add-btn">+ Add IMEI</button><label style="margin-top: 10px;">Or upload IMEI file</label><input type="file" id="cpr-file" name="upload"/></div><div class="cpr-panel"><h4>Non-Serialized Items</h4><div id="nonser-container"><div class="inline-row"><input type="number" name="returnItemId" class="return-itemId" placeholder="Item Id"min="1" step="1"/><input type="number" name="returnQty" class="return-qty" placeholder="Quantity" min="1"step="1"/><button type="button" class="btn btn-xs btn-default remove-row">×</button></div></div><button type="button" id="add-nonser" class="btn btn-xs btn-default cpr-add-btn">+ Add Item</button></div><div class="actions"><button type="button" id="cpr-submit" class="btn btn-primary">Create Purchase Return</button><a href="javascript:void(0);" class="btn btn-default unsettled-purchase-returns">Cancel</a></div></form></div></div></div></section><script type="text/javascript">$(document).off('.createpr');// Tab switch$(document).on('click.createpr', '.cpr-tab', function () {var tab = $(this).data('tab');$('.cpr-tab').removeClass('active');$(this).addClass('active');$('.cpr-tab-body').removeClass('active');$('#tab-' + tab).addClass('active');});// ===== Vendor autocomplete =====function filterVendorList() {var q = $('#cpr-vendor-search').val().toLowerCase().trim();var jqList = $('#cpr-vendor-list');if (!q) {jqList.hide();return;}var matches = 0;jqList.find('.item').each(function () {var hay = ($(this).data('name') + ' ' + $(this).data('id')).toLowerCase();var show = hay.indexOf(q) >= 0;$(this).toggle(show);if (show) matches++;});jqList.toggle(matches > 0);}$(document).on('focus.createpr input.createpr', '#cpr-vendor-search', filterVendorList);$(document).on('click.createpr', '#cpr-vendor-list .item', function () {var id = $(this).data('id');var name = $(this).data('name');$('#cpr-vendor').val(id);$('#cpr-vendor-search').val('');$('#cpr-vendor-list').hide();$('#cpr-vendor-selected .selected-label').text(name + ' (id=' + id + ')');$('#cpr-vendor-selected').show();});$(document).on('click.createpr', '#cpr-vendor-selected .clear-vendor', function () {$('#cpr-vendor').val('');$('#cpr-vendor-selected').hide();$('#cpr-vendor-search').val('').focus();});$(document).on('click.createpr', function (e) {if (!$(e.target).closest('.cpr-vendor-wrap').length) {$('#cpr-vendor-list').hide();}});function escapeHtml(v) {if (v === null || v === undefined) return '';return String(v).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');}function readImeiInput(callback) {var text = $('#bulk-imei-text').val() || '';var fileEl = $('#bulk-imei-file')[0];if (fileEl.files.length > 0) {var reader = new FileReader();reader.onload = function (e) {var combined = (text + '\n' + (e.target.result || '')).trim();callback(combined);};reader.readAsText(fileEl.files[0]);} else {callback(text.trim());}}$(document).on('click.createpr', '#bulk-verify', function () {var bwh = $('#cpr-bwh').val();if (!bwh) {alert('Please select a billing warehouse first.');return;}readImeiInput(function (imeiStr) {if (!imeiStr) {alert('Paste IMEIs or upload a file.');return;}$('#bulk-preview').html('Verifying...');$('#bulk-invalid').html('');$('#bulk-create').prop('disabled', true);$.ajax({type: 'POST',url: context + "/return/unsettled/verify-imeis",data: {imeis: imeiStr, billingWarehouseId: bwh},success: function (resp) {var r = typeof resp === 'string' ? JSON.parse(resp) : resp;renderBulkPreview(r);},error: function (xhr) {$('#bulk-preview').html('');$('#bulk-invalid').text('Verify failed: ' + (xhr.responseText || 'server error'));}});});});function renderBulkPreview(r) {var groups = r.groups || [];var invalid = r.invalidImeis || [];var html = '';if (groups.length === 0) {html = '<div class="alert alert-warning">No valid IMEI groups found.</div>';} else {html += '<div class="bulk-grand">' + groups.length + ' purchase return(s) will be created. Grand total: ' + escapeHtml(r.grandTotal) + '</div>';html += '<table><thead><tr><th>#</th><th>Invoice</th><th>Vendor</th><th>Qty</th><th>Items & IMEIs</th><th>Total</th></tr></thead><tbody>';for (var i = 0; i < groups.length; i++) {var g = groups[i];var imeis = g.imeis || [];// Sub-group IMEIs by product name + unit price, so repeated items show as "Product x N"var subMap = {};var subOrder = [];imeis.forEach(function (e) {var key = (e.productName || '(unknown)') + '||' + e.unitPrice;if (!subMap[key]) {subMap[key] = {name: e.productName || '(unknown)', unitPrice: e.unitPrice, imeis: []};subOrder.push(key);}subMap[key].imeis.push(e.imei);});var subHtml = subOrder.map(function (k) {var s = subMap[k];return '<div><strong>' + escapeHtml(s.name) + '</strong> × ' + s.imeis.length+ ' @ ' + escapeHtml(s.unitPrice)+ '<br/><small style="color:#666;">' + s.imeis.map(escapeHtml).join(', ') + '</small></div>';}).join('<hr style="margin:4px 0;"/>');html += '<tr>'+ '<td>' + (i + 1) + '</td>'+ '<td>' + escapeHtml(g.invoiceNumber || '-') + '</td>'+ '<td>' + escapeHtml(g.vendorName) + ' (id=' + escapeHtml(g.vendorId) + ')</td>'+ '<td>' + imeis.length + '</td>'+ '<td>' + subHtml + '</td>'+ '<td>' + escapeHtml(g.totalAmount) + '</td>'+ '</tr>';}html += '</tbody></table>';}$('#bulk-preview').html(html);if (invalid.length > 0) {$('#bulk-invalid').html('<strong>Invalid IMEIs:</strong><br/>' + invalid.map(escapeHtml).join('<br/>'));} else {$('#bulk-invalid').html('');}$('#bulk-create').prop('disabled', groups.length === 0 || invalid.length > 0);}$(document).on('click.createpr', '#bulk-create', function () {var jqBtn = $(this);if (jqBtn.data('submitting')) {return;}var bwh = $('#cpr-bwh').val();var reasonType = $('#cpr-reason-type').val();var reasonText = $('#cpr-reason-text').val() || '';if (!bwh) {alert('Select billing warehouse.');return;}readImeiInput(function (imeiStr) {if (!imeiStr) {alert('No IMEIs to submit.');return;}if (!confirm('Create purchase returns + debit notes for these groups?')) return;jqBtn.data('submitting', true).prop('disabled', true);$.ajax({type: 'POST',url: context + "/return/unsettled/create-bulk",data: {imeis: imeiStr, billingWarehouseId: bwh, reasonType: reasonType, reasonText: reasonText},success: function (resp) {var r = typeof resp === 'string' ? JSON.parse(resp) : resp;if (r && r.status === true) {var msg = 'Created ' + (r.created || []).length + ' purchase return(s) + debit note(s).';(r.created || []).forEach(function (p) {msg += '\nPR #' + p.purchaseReturnId + ' / DN #' + p.debitNoteId;});alert(msg);doAjaxRequestHandler(context + "/return/unsettled", "GET", function (lr) {$('#main-content').html(lr);});} else {alert((r && r.message) ? r.message : 'Bulk create failed.');jqBtn.data('submitting', false).prop('disabled', false);}},error: function (xhr) {var msg = 'Bulk create failed.';try {var body = JSON.parse(xhr.responseText);if (body && body.response && body.response.message) msg = body.response.message;else if (body && body.message) msg = body.message;} catch (e) {if (xhr.responseText) msg = xhr.responseText;}alert(msg);jqBtn.data('submitting', false).prop('disabled', false);}});});});// ===== Manual tab (existing flow) =====$(document).on('click.createpr', '#add-imei', function () {$('#imei-container').append('<div class="inline-row">' +'<input type="text" name="returnImeiNumber" class="imei-number" placeholder="IMEI Number"/>' +'<button type="button" class="btn btn-xs btn-default remove-row">×</button>' +'</div>');});$(document).on('click.createpr', '#add-nonser', function () {$('#nonser-container').append('<div class="inline-row">' +'<input type="number" name="returnItemId" class="return-itemId" placeholder="Item Id" min="1" step="1"/>' +'<input type="number" name="returnQty" class="return-qty" placeholder="Quantity" min="1" step="1"/>' +'<button type="button" class="btn btn-xs btn-default remove-row">×</button>' +'</div>');});$(document).on('click.createpr', '#create-pr-form .remove-row', function () {var jqRow = $(this).closest('.inline-row');if (jqRow.siblings('.inline-row').length === 0) {jqRow.find('input').val('');} else {jqRow.remove();}});$(document).on('click.createpr', '#cpr-submit', function () {var jqBtn = $(this);if (jqBtn.data('submitting')) {return;}var vendorId = $('#cpr-vendor').val();var bwh = $('#cpr-bwh').val();if (!vendorId) {alert('Please select a vendor.');return;}if (!bwh) {alert('Please select a billing warehouse.');return;}jqBtn.data('submitting', true).prop('disabled', true).addClass('disabled');var formData = new FormData(document.getElementById('create-pr-form'));formData.append('billingWarehouseId', bwh);formData.append('reasonType', $('#cpr-reason-type').val());formData.append('reasonText', $('#cpr-reason-text').val() || '');$.ajax({type: 'POST', url: context + "/return/unsettled/create",data: formData, cache: false, contentType: false, processData: false,success: function (response) {var parsed = response;try {parsed = typeof response === 'string' ? JSON.parse(response) : response;} catch (e) {}if (parsed && parsed.status === true) {alert('Purchase Return created. ID: ' + parsed.purchaseReturnId);doAjaxRequestHandler(context + "/return/unsettled", "GET", function (resp) {$('#main-content').html(resp);});} else {alert((parsed && parsed.message) ? parsed.message : 'Failed to create.');jqBtn.data('submitting', false).prop('disabled', false).removeClass('disabled');}},error: function (xhr) {var msg = 'Create failed.';try {var body = JSON.parse(xhr.responseText);if (body && body.response && body.response.message) msg = body.response.message;else if (body && body.message) msg = body.message;} catch (e) {if (xhr.responseText) msg = xhr.responseText;}alert(msg);jqBtn.data('submitting', false).prop('disabled', false).removeClass('disabled');}});});</script>