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
|
'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>
|
||||||
@@ -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