Add pallet-list page and Pallet form auto-fill
This commit is contained in:
@@ -11,5 +11,61 @@ frappe.ui.form.on('Pallet', {
|
||||
'pallet': frm.doc.pallet_number || frm.doc.name
|
||||
});
|
||||
}, __('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>
|
||||
@@ -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, "<").replace(/>/g, ">");
|
||||
|
||||
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>« 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 »</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}"}
|
||||
Reference in New Issue
Block a user