Add pallet-list page and Pallet form auto-fill

This commit is contained in:
Westech Admin
2026-05-19 20:57:35 +00:00
parent 2a2dccdcdc
commit 73e7df4735
6 changed files with 317 additions and 0 deletions
+56
View File
@@ -11,5 +11,61 @@ frappe.ui.form.on('Pallet', {
'pallet': frm.doc.pallet_number || frm.doc.name 'pallet': frm.doc.pallet_number || frm.doc.name
}); });
}, __('View')); }, __('View'));
},
customer_number: function(frm) {
var customer = frm.doc.customer_number;
if (!customer) {
clear_customer_fields(frm);
return;
}
frappe.call({
method: 'frappe.client.get',
args: {doctype: 'Supplier', name: customer},
callback: function(r) {
if (!r.message) return;
var s = r.message;
if (!frm.doc.supplier) {
frm.set_value('supplier', s.name);
}
if (!frm.doc.company_name && s.supplier_name) {
frm.set_value('company_name', s.supplier_name);
}
if (s.supplier_primary_contact) {
frappe.call({
method: 'frappe.client.get',
args: {doctype: 'Contact', name: s.supplier_primary_contact},
callback: function(cr) {
if (!cr.message) return;
var ct = cr.message;
var full_name = [ct.first_name, ct.last_name].filter(Boolean).join(' ');
if (!frm.doc.contact_name) frm.set_value('contact_name', full_name);
if (!frm.doc.contact_number) frm.set_value('contact_number', ct.phone || ct.mobile_no || '');
if (!frm.doc.contact_email) frm.set_value('contact_email', ct.email_id || '');
} }
}); });
}
if (s.supplier_primary_address) {
frappe.call({
method: 'frappe.client.get',
args: {doctype: 'Address', name: s.supplier_primary_address},
callback: function(ar) {
if (!ar.message) return;
var a = ar.message;
if (!frm.doc.address_line) frm.set_value('address_line', a.address_line1 || '');
}
});
}
}
});
}
});
function clear_customer_fields(frm) {
frm.set_value('supplier', '');
frm.set_value('company_name', '');
frm.set_value('contact_name', '');
frm.set_value('contact_number', '');
frm.set_value('contact_email', '');
frm.set_value('address_line', '');
}
@@ -0,0 +1,51 @@
<style>
.pallet-table-container { padding: 0; overflow-x: auto; }
.pallet-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.pallet-table th { background: #3cc062; color: white; padding: 8px 10px; text-align: left; cursor: pointer; }
.pallet-table td { padding: 5px 10px; border-bottom: 1px solid #eee; }
.pallet-table tr:hover { background: #f5f5f5; }
.status-received { color: #2196F3; }
.status-sorting { color: #FF9800; }
.status-processing { color: #9C27B0; }
.status-complete { color: #4CAF50; }
.status-shipped { color: #607D8B; }
</style>
<div class="search-box">
<input type="text" id="pallet-search" placeholder="Search pallet #...">
<select id="status-filter">
<option value="">All</option>
<option value="Received">Received</option>
<option value="Sorting">Sorting</option>
<option value="Processing">Processing</option>
<option value="Complete">Complete</option>
<option value="Shipped">Shipped</option>
</select>
</div>
<div class="pallet-table-container">
<table class="pallet-table" id="pallet-table">
<thead>
<tr>
<th>Pallet #</th>
<th>Date Reserved</th>
<th>Rec. Date</th>
<th>Customer #</th>
<th>Company</th>
<th>Lbs</th>
<th>Who</th>
<th>Items</th>
<th>QTY Sale</th>
<th>Lbs Sale</th>
<th>Finish Date</th>
<th>Status</th>
<th>Notes</th>
</tr>
</thead>
<tbody id="pallet-tbody">
<tr><td colspan="13" style="text-align:center;padding:40px;">Loading...</td></tr>
</tbody>
</table>
</div>
<div id="pallet-pagination" style="margin-top:12px;"\u003e</div>
+128
View File
@@ -0,0 +1,128 @@
frappe.pages["pallet-list"].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
title: __("Pallet List"),
single_column: true
});
var content = frappe.render_template("pallet-list", {});
$(page.body).append(content);
var currentPage = 1;
var pageSize = 100;
var currentSort = "pallet_number";
var sortDir = "desc";
var searchTerm = "";
var statusFilter = "";
function loadPallets() {
frappe.call({
method: "westech_r2.page.pallet-list.pallet-list.get_pallets",
args: {
page: currentPage,
page_size: pageSize,
sort_field: currentSort,
sort_dir: sortDir,
status_filter: statusFilter,
search: searchTerm
},
callback: function(r) {
if (r.message) {
renderPallets(r.message.pallets, r.message.total);
}
}
});
}
function renderPallets(pallets, total) {
var tbody = $("#pallet-tbody");
tbody.empty();
if (!pallets || pallets.length === 0) {
tbody.append('<tr><td colspan="13" style="text-align:center;padding:40px;">No pallets found</td></tr>');
return;
}
pallets.forEach(function(p) {
var statusClass = "status-" + (p.status || "").toLowerCase().replace(/\s+/g, "-");
var link = "/app/pallet/" + encodeURIComponent(p.name);
var pn = (p.pallet_number || "").replace(/</g, "&lt;").replace(/>/g, "&gt;");
var row = '<tr>' +
'<td><a href="' + link + '" style="color:#3cc062;text-decoration:none;font-weight:600;">' + pn + '</a></td>' +
'<td>' + fmtDate(p.date_reserved) + '</td>' +
'<td>' + fmtDate(p.received_date) + '</td>' +
'<td>' + (p.customer_number || "") + '</td>' +
'<td>' + (p.company_name || "") + '</td>' +
'<td>' + (p.inbound_weight || "") + '</td>' +
'<td>' + (p.tester || "") + '</td>' +
'<td>' + (p.description || "") + '</td>' +
'<td>' + (p.qty_to_sales || "") + '</td>' +
'<td>' + (p.weight_to_sales || "") + '</td>' +
'<td>' + fmtDate(p.finish_date) + '</td>' +
'<td class="' + statusClass + '">' + (p.status || "") + '</td>' +
'<td>' + (p.notes || "").substring(0, 50) + '</td>' +
'</tr>';
tbody.append(row);
});
renderPagination(total);
}
function renderPagination(total) {
var totalPages = Math.ceil(total / pageSize);
var pagination = $("#pallet-pagination");
pagination.empty();
if (totalPages <= 1) return;
var prevBtn = $('<button>&laquo; Prev</button>').attr("disabled", currentPage === 1)
.css({padding: "5px 12px", border: "1px solid #ddd", background: "white", cursor: "pointer", marginRight: "5px"})
.on("click", function() {
if (currentPage > 1) { currentPage--; loadPallets(); }
});
pagination.append(prevBtn);
var startPage = Math.max(1, currentPage - 2);
var endPage = Math.min(totalPages, startPage + 4);
for (var i = startPage; i <= endPage; i++) {
var btn = $('<button>' + i + '</button>')
.css({padding: "5px 12px", border: "1px solid #ddd", background: i === currentPage ? "#3cc062" : "white",
color: i === currentPage ? "white" : "#333", cursor: "pointer", marginRight: "5px"})
.on("click", function(page) {
return function() { currentPage = page; loadPallets(); };
}(i));
pagination.append(btn);
}
var nextBtn = $('<button>Next &raquo;</button>').attr("disabled", currentPage === totalPages)
.css({padding: "5px 12px", border: "1px solid #ddd", background: "white", cursor: "pointer", marginRight: "5px"})
.on("click", function() {
if (currentPage < totalPages) { currentPage++; loadPallets(); }
});
pagination.append(nextBtn);
}
function fmtDate(v) {
if (!v) return "";
var s = String(v);
if (s.indexOf("T") > -1) s = s.split("T")[0];
if (s.indexOf(" ") > -1) s = s.split(" ")[0];
return s;
}
$("#pallet-search").on("input", function() {
searchTerm = $(this).val();
currentPage = 1;
loadPallets();
});
$("#status-filter").on("change", function() {
statusFilter = $(this).val();
currentPage = 1;
loadPallets();
});
loadPallets();
};
@@ -0,0 +1,19 @@
{
content: null,
creation: 2026-05-19 13:00:00.000000,
docstatus: 0,
doctype: Page,
idx: 0,
modified: 2026-05-19 13:00:00.000000,
modified_by: Administrator,
module: Westech R2,
name: pallet-list,
owner: Administrator,
page_name: pallet-list,
roles: [],
script: null,
standard: Yes,
style: null,
system_page: 0,
title: Pallet List
}
@@ -0,0 +1,63 @@
import frappe
import json
@frappe.whitelist()
def get_pallets(page=1, page_size=100, sort_field="pallet_number", sort_dir="desc", status_filter="", search=""):
"""Fetch pallets from ERPNext with filters."""
page = int(page)
page_size = int(page_size)
offset = (page - 1) * page_size
# Base filters
filters = [
["Pallet", "pallet_number", "is", "set"],
["Pallet", "date_reserved", "is", "set"],
]
if status_filter:
filters.append(["Pallet", "status", "=", status_filter])
if search:
filters.append(["Pallet", "pallet_number", "like", f"%{search}%"])
fields = [
"name", "pallet_number", "date_reserved", "received_date",
"customer_number", "company_name", "inbound_weight", "tester",
"description", "qty_to_sales", "weight_to_sales", "finish_date", "notes", "status"
]
order_by = f"{sort_field} {sort_dir}"
# Get total count
count_result = frappe.get_list(
"Pallet",
filters=filters,
fields=["count(*) as total"],
as_list=True
)
total = count_result[0][0] if count_result else 0
# Get pallets
pallets = frappe.get_list(
"Pallet",
filters=filters,
fields=fields,
order_by=order_by,
limit_page_length=page_size,
limit_start=offset
)
return {
"pallets": pallets,
"total": total,
"page": page,
"page_size": page_size
}
@frappe.whitelist()
def update_pallet(docname, field, value):
"""Update a single field on a Pallet."""
pallet = frappe.get_doc("Pallet", docname)
pallet.set(field, value)
pallet.save(ignore_permissions=True)
frappe.db.commit()
return {"status": "ok", "message": f"Updated {field}"}