413 lines
17 KiB
JavaScript
413 lines
17 KiB
JavaScript
function debugDataStructure(data) {
|
|
console.group('Data Structure Analysis');
|
|
console.log('Raw data:', data);
|
|
console.log('Type:', typeof data);
|
|
console.log('Is Array:', Array.isArray(data));
|
|
console.log('Constructor:', data?.constructor?.name);
|
|
|
|
if (data && typeof data === 'object') {
|
|
console.log('Object keys:', Object.keys(data));
|
|
|
|
// Check if it's a wrapped response
|
|
if (data.data) console.log('data.data:', data.data);
|
|
if (data.result) console.log('data.result:', data.result);
|
|
if (data.items) console.log('data.items:', data.items);
|
|
|
|
// Sample first element if it's array-like
|
|
if (Array.isArray(data) && data.length > 0) {
|
|
console.log('First element:', data[0]);
|
|
console.log('First element keys:', Object.keys(data[0]));
|
|
}
|
|
}
|
|
console.groupEnd();
|
|
}
|
|
function viewItemToPrint() {
|
|
loader = $('#overlay, #loader').css('z-index', 1090);
|
|
$('#viewIteToPrint').modal('show');
|
|
$('#viewIteToPrint').css('z-index', 1050);
|
|
let PRNo = document.getElementById('label-prNo').innerHTML;
|
|
document.getElementById('label-print-prNo').innerHTML = PRNo;
|
|
tableName = '#selectPrintTable';
|
|
totalSelectedLabel = $('#totalSelectPrint');
|
|
|
|
clearTableSelection(tableName, selectedProductsMap, () => {
|
|
totalSelectedLabel.text(0);
|
|
}, 'selected-row', '.select-all-PrintItem-checkbox');
|
|
|
|
tableElement = $(tableName);
|
|
tableDestroy(tableElement);
|
|
let IsTagging = true;
|
|
var canvassPrinting = tableElement.DataTable({
|
|
ajax: $.extend({
|
|
url: endpoint.GetCanvassGroupByPRNo,
|
|
type: 'POST',
|
|
data: { PRNo, IsTagging }
|
|
}, beforeComplete(loader)),
|
|
language: {
|
|
emptyTable: "No record available"
|
|
},
|
|
initComplete: function () {
|
|
initializeTableSelection({
|
|
tableName: tableName,
|
|
dataTable: canvassPrinting,
|
|
selectedItemsMap: selectedProductsMap,
|
|
idKey: 'itemNo',
|
|
idKey2: 'prDetailsId',
|
|
checkboxClass: '.select-PrintItem-checkbox',
|
|
selectAllClass: '.select-all-PrintItem-checkbox',
|
|
selectedRowClass: 'selected-row',
|
|
updateCountCallback: function () {
|
|
totalSelectedLabel.text(getSelectedCount(selectedProductsMap));
|
|
}
|
|
});
|
|
},
|
|
columns: colCanvassItemToPrint,
|
|
error: errorHandler
|
|
});
|
|
}
|
|
function printPRNo(PRNo, IsTagging, AggreItemNo) {
|
|
return new Promise((resolve, reject) => {
|
|
|
|
$('#canvasContent').html('<div class="loading">Loading canvas data...</div>');
|
|
$.ajax({
|
|
url: endpoint.GetCanvassGroupByPRNo,
|
|
type: 'GET',
|
|
data: { PRNo, IsTagging, AggreItemNo },
|
|
success: function (data) {
|
|
try {
|
|
generateGroupedTable(data);
|
|
resolve();
|
|
loader.hide();
|
|
} catch (error) {
|
|
console.error('Error generating table:', error);
|
|
loader.hide();
|
|
$('#canvasContent').html(`
|
|
<div class="error">
|
|
<h4>Error generating table:</h4>
|
|
<p><strong>Error:</strong> ${error.message}</p>
|
|
<p><strong>Data received:</strong> ${JSON.stringify(data, null, 2)}</p>
|
|
<details>
|
|
<summary>Full Error Details</summary>
|
|
<pre>${error.stack}</pre>
|
|
</details>
|
|
</div>
|
|
`);
|
|
reject(error);
|
|
}
|
|
},
|
|
error: function (xhr, status, error) {
|
|
console.error('AJAX Error:', error);
|
|
$('#canvasContent').html('<div class="error">Error loading data: ' + error + '</div>');
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
function generateGroupedTable(data) {
|
|
// Debug: Log the received data to console
|
|
/*
|
|
|
|
console.log('Is Array:', Array.isArray(data));
|
|
*/
|
|
// Handle different response formats
|
|
let processedData = data;
|
|
//console.log('Data type:', typeof data);
|
|
//console.log('Received data:', data);
|
|
// If data is wrapped in an object (common with ASP.NET Core)
|
|
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
if (data.data && Array.isArray(data.data)) {
|
|
processedData = data.data;
|
|
} else if (data.result && Array.isArray(data.result)) {
|
|
processedData = data.result;
|
|
} else if (data.items && Array.isArray(data.items)) {
|
|
processedData = data.items;
|
|
} else {
|
|
// Try to find the first array property
|
|
for (let key in data) {
|
|
if (Array.isArray(data[key])) {
|
|
processedData = data[key];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Final validation
|
|
if (!Array.isArray(processedData)) {
|
|
console.error('Data is not an array:', processedData);
|
|
$('#canvasContent').html('<div class="error">Invalid data format received from server. Expected array but got: ' + typeof processedData + '</div>');
|
|
return;
|
|
}
|
|
|
|
if (processedData.length === 0) {
|
|
$('#canvasContent').html('<div class="error">No data found for the specified PR Number.</div>');
|
|
return;
|
|
}
|
|
|
|
// Group data by item (itemName + itemNo)
|
|
const groupedData = {};
|
|
|
|
// Debug: Log each row to see the actual data structure
|
|
// console.log('Processing', processedData.length, 'rows');
|
|
|
|
processedData.forEach((row, index) => {
|
|
// console.log(`Row ${index}:`, row);
|
|
|
|
// Handle different property name formats (camelCase vs PascalCase)
|
|
const itemName = row.itemName || row.ItemName || '';
|
|
const itemNo = row.itemNo || row.ItemNo || '';
|
|
const supplierId = row.supplierId || row.supplierId || 0;
|
|
const supplierName = row.supplierName || row.SupplierName || '';
|
|
const previousPrice = row.previousPrice || row.PreviousPrice || 0;
|
|
const qty = row.qty || row.Qty || 0;
|
|
const unit = row.uomName || row.UOMName || 'pcs';
|
|
const unitPrice = row.unitPrice || row.UnitPrice || 0;
|
|
const discount = row.discount || row.Discount || 0;
|
|
const totalAmount = row.totalAmount || row.TotalAmount || 0;
|
|
const terms = row.terms || row.Terms || '';
|
|
const remarks = row.remarks || row.Remarks || '';
|
|
const leadTime = row.leadTime || row.LeadTime || '';
|
|
const isVatable = row.isVatable || row.IsVatable || false;
|
|
const previousPricePO = row.previousPricePO || row.PreviousPricePO || 0;
|
|
|
|
// Debug: Log extracted values
|
|
/* console.log(`Extracted values for row ${index}:`, {
|
|
itemName, itemNo, supplierName, terms, remarks, leadTime, isVatable
|
|
});*/
|
|
|
|
const groupKey = `${itemNo}|${itemName}`;
|
|
if (!groupedData[groupKey]) {
|
|
groupedData[groupKey] = {
|
|
itemName: itemName,
|
|
itemNo: itemNo,
|
|
previousPricePO: previousPricePO,
|
|
suppliers: new Map() // Use Map to avoid duplicates
|
|
};
|
|
}
|
|
|
|
// Use supplier name as key to avoid duplicates, but handle empty supplier names
|
|
const supplierKey = supplierId ? supplierId : `supplier_${index}`;
|
|
|
|
if (!groupedData[groupKey].suppliers.has(supplierKey)) {
|
|
groupedData[groupKey].suppliers.set(supplierKey, {
|
|
supplierName: supplierName,
|
|
previousPrice: previousPrice,
|
|
qty: qty,
|
|
unit: unit,
|
|
unitPrice: unitPrice,
|
|
discount: discount,
|
|
totalAmount: totalAmount,
|
|
terms: terms,
|
|
remarks: remarks,
|
|
leadTime: leadTime,
|
|
isVatable: isVatable
|
|
});
|
|
|
|
/* console.log(`Added supplier ${supplierKey} to group ${groupKey}:`, {
|
|
terms, remarks, leadTime, isVatable
|
|
});*/
|
|
} else {
|
|
// Handle duplicate supplier case
|
|
const existing = groupedData[groupKey].suppliers.get(supplierKey);
|
|
if (unitPrice < existing.unitPrice && unitPrice > 0) {
|
|
groupedData[groupKey].suppliers.set(supplierKey, {
|
|
supplierName: supplierName,
|
|
previousPrice: previousPrice,
|
|
qty: qty,
|
|
unit: unit,
|
|
unitPrice: unitPrice,
|
|
discount: discount,
|
|
totalAmount: totalAmount,
|
|
terms: terms,
|
|
remarks: remarks,
|
|
leadTime: leadTime,
|
|
isVatable: isVatable
|
|
});
|
|
|
|
/* console.log(`Updated supplier ${supplierKey} in group ${groupKey}:`, {
|
|
terms, remarks, leadTime, isVatable
|
|
});*/
|
|
}
|
|
}
|
|
});
|
|
|
|
// Debug: Log final grouped data
|
|
// console.log('Final grouped data:', groupedData);
|
|
|
|
// Generate HTML
|
|
let html = '';
|
|
|
|
Object.keys(groupedData).sort().forEach(groupKey => {
|
|
const group = groupedData[groupKey];
|
|
|
|
html += `
|
|
<div class="item-group">
|
|
<div class="item-header">
|
|
<div class="item-info">
|
|
<h3>Item: ${escapeHtml(group.itemName)}</h3>
|
|
<p>ItemCode: ${escapeHtml(group.itemNo)}</p>
|
|
</div>
|
|
${group.previousPricePO ? `
|
|
<div class="previous-price-info">
|
|
<span class="label">Previous Price base on latest PO's</span>
|
|
<span class="value">${escapeHtml(group.previousPricePO)}</span>
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
|
|
<table class="suppliers-table" width="100%" style="table-layout:fixed">
|
|
<colgroup>
|
|
<col style="width:17%">
|
|
<col style="width:7%">
|
|
<col style="width:7%">
|
|
<col style="width:7%">
|
|
<col style="width:7%">
|
|
<col style="width:8%">
|
|
<col style="width:12%">
|
|
<col style="width:7%">
|
|
<col style="width:6%">
|
|
<col style="width:22%">
|
|
</colgroup>
|
|
<thead>
|
|
<tr>
|
|
<th>Supplier</th>
|
|
<th>Prev.Price</th>
|
|
<th>Qty</th>
|
|
<th>UnitPrice</th>
|
|
<th>Discount</th>
|
|
<th>TotalPrice</th>
|
|
<th>Terms</th>
|
|
<th>LeadTime</th>
|
|
<th>Vatable</th>
|
|
<th>Remarks</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
`;
|
|
|
|
// Convert Map to Array and sort suppliers by unit price (ascending)
|
|
const suppliersArray = Array.from(group.suppliers.values());
|
|
suppliersArray.sort((a, b) => {
|
|
// Sort by unit price (ascending), then by supplier name
|
|
if (a.unitPrice !== b.unitPrice) {
|
|
return (a.unitPrice || 0) - (b.unitPrice || 0);
|
|
}
|
|
return (a.supplierName || '').localeCompare(b.supplierName || '');
|
|
});
|
|
|
|
suppliersArray.forEach((supplier, supplierIndex) => {
|
|
// Auto-compute total amount: qty * unitPrice, or 0 if no offer (unitPrice is 0 or null)
|
|
let computedTotal = 0;
|
|
if (supplier.unitPrice && supplier.unitPrice > 0 && supplier.qty && supplier.qty > 0) {
|
|
computedTotal = parseFloat(supplier.qty) * parseFloat(supplier.unitPrice);
|
|
}
|
|
|
|
// Debug: Log each supplier's data being rendered
|
|
/* console.log(`Rendering supplier ${supplierIndex} for group ${groupKey}:`, {
|
|
supplierName: supplier.supplierName,
|
|
terms: supplier.terms,
|
|
remarks: supplier.remarks,
|
|
leadTime: supplier.leadTime,
|
|
isVatable: supplier.isVatable
|
|
});*/
|
|
|
|
html += `
|
|
<tr>
|
|
<td style="word-wrap: break-word;max-width: 120px;">${escapeHtml(supplier.supplierName || '')}</td>
|
|
<td class="text-center">${formatPrice(supplier.previousPrice)}</td>
|
|
<td class="text-center">${formatQuantity(supplier.qty, supplier.unit)}</td>
|
|
<td class="text-center">${formatPrice(supplier.unitPrice)}</td>
|
|
<td class="text-center">${escapeHtml(supplier.discount || '')}</td>
|
|
<td class="text-center">${formatPrice(computedTotal)}</td>
|
|
<td style="word-wrap: break-word; max-width: 30px;text-center">${escapeHtml(supplier.terms || '')}</td>
|
|
<td class="text-center">${escapeHtml(supplier.leadTime || '')}</td>
|
|
<td class="text-center">${supplier.isVatable ? 'YES' : 'NO'}</td>
|
|
<td style="word-wrap: break-word; max-width: 120px;text-left">${escapeHtml(supplier.remarks || '')}</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
|
|
html += `
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
$('#canvasContent').html(html);
|
|
}
|
|
function formatPrice(value) {
|
|
if (!value || value === 0) return '0.00';
|
|
return parseFloat(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
}
|
|
function formatQuantity(qty, unit) {
|
|
if (!qty) return '';
|
|
return parseFloat(qty).toFixed(0) + ' ' + (unit || 'pcs');
|
|
}
|
|
function escapeHtml(text) {
|
|
if (!text) return '';
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
function printRelatedItem() {
|
|
loader = $('#overlay, #loader').css('z-index', 1080);
|
|
let PRNo = document.getElementById('label-prNo').innerHTML;
|
|
|
|
const selectedItems = Object.values(selectedProductsMap);
|
|
|
|
if (selectedItems.length === 0) {
|
|
showToast('warning', 'Please select items for canvass first!', 'warning', 4000);
|
|
return;
|
|
}
|
|
const AggreItemNo = selectedItems.map(item => item.itemNo).join(',');
|
|
|
|
printPRNo(PRNo, false, AggreItemNo).then(() => {
|
|
document.getElementById('print-prNo').innerHTML = PRNo;
|
|
const iframe = document.createElement('iframe');
|
|
iframe.style.position = 'fixed';
|
|
iframe.style.right = '0';
|
|
iframe.style.bottom = '0';
|
|
iframe.style.width = '0';
|
|
iframe.style.height = '0';
|
|
iframe.style.border = 'none';
|
|
document.body.appendChild(iframe);
|
|
|
|
const doc = iframe.contentWindow.document;
|
|
doc.open();
|
|
doc.write('<html><head><title>Print</title>');
|
|
|
|
document.querySelectorAll('link[rel="stylesheet"]').forEach(sheet => doc.write(sheet.outerHTML));
|
|
document.querySelectorAll('style').forEach(style => doc.write(style.outerHTML));
|
|
|
|
doc.write(`
|
|
<style>
|
|
@media print {
|
|
* { -webkit-print-color-adjust: exact !important; color-adjust: exact !important; print-color-adjust: exact !important; }
|
|
}
|
|
body { font-family: "Georgia", serif; font-size: 12px; margin: 0; padding: 5px; }
|
|
.header { font-size: 12px; font-weight: bold; text-align: center; margin-bottom: 20px; padding: 10px 0; }
|
|
.dataTables_length, .dataTables_filter, .dataTables_info, .dataTables_paginate { display: none !important; }
|
|
table { width: 100% !important; border-collapse: collapse; font-size: 8px; font-family: "Georgia", serif; }
|
|
table th, table td { padding: 1px 2px; line-height: .9; vertical-align: top; border: .5px solid #808080 !important; }
|
|
.pr-info-section { font-size: 10px !important; }
|
|
.pr-info-section label, .pr-info-section strong { font-size: 9px !important; }
|
|
</style>
|
|
`);
|
|
|
|
doc.write('</head><body>');
|
|
doc.write('<div class="header">Canvass Report</div>');
|
|
|
|
const printContent = document.getElementById('printableRelatedItem').cloneNode(true);
|
|
doc.write(printContent.outerHTML);
|
|
doc.write('</body></html>');
|
|
doc.close();
|
|
|
|
iframe.onload = function () {
|
|
iframe.contentWindow.print();
|
|
setTimeout(() => document.body.removeChild(iframe), 1000);
|
|
};
|
|
}).catch(err => {
|
|
console.error('Error loading data:', err);
|
|
});
|
|
} |