feat: intake page rework, new Pallet fields, address fix, button rename, COR generator, theme CSS

This commit is contained in:
Westech Admin
2026-05-22 05:15:09 +00:00
parent 3c11969c89
commit be06bca0cf
16 changed files with 419 additions and 800 deletions
@@ -1,82 +0,0 @@
<style>
.pallet-list-page { font-family: Helvetica Neue, Arial, sans-serif; }
.pallet-list-page h2 { color: #2F5496; margin-bottom: 16px; font-size: 20px; }
.pallet-search-box { margin-bottom: 16px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.pallet-search-box input, .pallet-search-box select {
padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px;
}
.pallet-search-box button {
padding: 6px 14px; border: 1px solid #ddd; border-radius: 4px; cursor: pointer;
font-size: 13px; background: white;
}
.pallet-search-box button.btn-primary { background: #2F5496; color: white; border-color: #2F5496; }
.pallet-search-box button.btn-success { background: #548235; color: white; border-color: #548235; }
.pallet-table-container { padding: 0; overflow-x: auto; background: white; border-radius: 4px; }
.pallet-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.pallet-table th {
background: #2F5496; color: white; padding: 8px 10px; text-align: left;
cursor: pointer; white-space: nowrap; font-weight: 500;
}
.pallet-table td { padding: 5px 10px; border-bottom: 1px solid #eee; }
.pallet-table tr:hover { background: #f5f5f5; }
.pallet-table tr.new-row { background: #FFF9E6 !important; }
.pallet-table a { color: #2F5496; text-decoration: none; font-weight: 600; }
.status-received { color: #2196F3; font-weight: 600; }
.status-sorting { color: #FF9800; font-weight: 600; }
.status-processing { color: #9C27B0; font-weight: 600; }
.status-complete { color: #4CAF50; font-weight: 600; }
.status-shipped { color: #607D8B; font-weight: 600; }
.pallet-pagination { margin-top: 12px; display: flex; gap: 5px; align-items: center; }
.pallet-pagination button {
padding: 5px 12px; border: 1px solid #ddd; background: white; cursor: pointer;
border-radius: 4px; font-size: 13px; color: #2F5496;
}
.pallet-pagination button.active { background: #2F5496; color: white; border-color: #2F5496; }
.pallet-pagination button:disabled { opacity: 0.5; cursor: not-allowed; }
.pallet-count { margin-left: auto; color: #666; font-size: 13px; }
</style>
<div class="pallet-list-page">
<h2>📦 Pallet List</h2>
<div class="pallet-search-box">
<input type="text" id="pallet-search" placeholder="Search pallet #..." style="max-width:200px;">
<select id="status-filter">
<option value="">All Statuses</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>
<button class="btn btn-default" id="btn-clear">✕ Clear</button>
<button class="btn btn-success" id="btn-export">⬇ Export CSV</button>
<span class="pallet-count" id="pallet-count"></span>
</div>
<div class="pallet-table-container">
<table class="pallet-table" id="pallet-table">
<thead>
<tr>
<th data-sort="pallet_number">Pallet # ⇅</th>
<th data-sort="date_reserved">Date Reserved ⇅</th>
<th data-sort="received_date">Rec. Date ⇅</th>
<th data-sort="customer_number">Customer # ⇅</th>
<th data-sort="inbound_weight">Lbs ⇅</th>
<th data-sort="tester">Who ⇅</th>
<th data-sort="description">Items Tested ⇅</th>
<th data-sort="qty_to_sales">QTY Sale ⇅</th>
<th data-sort="weight_to_sales">Lbs Sale ⇅</th>
<th data-sort="finish_date">Finish Date ⇅</th>
<th>Status</th>
<th>Notes</th>
</tr>
</thead>
<tbody id="pallet-tbody">
<tr><td colspan="12" style="text-align:center;padding:40px;color:#666;">Loading...</td></tr>
</tbody>
</table>
</div>
<div class="pallet-pagination" id="pallet-pagination"></div>
</div>
-128
View File
@@ -1,128 +0,0 @@
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();
};
@@ -1,19 +0,0 @@
{
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
}
@@ -1,64 +0,0 @@
import frappe
@frappe.whitelist()
def get_pallets(page=1, page_size=100, sort_field="pallet_number", sort_dir="desc", status_filter="", search=""):
page = int(page)
page_size = int(page_size)
offset = (page - 1) * page_size
conditions = [
"pallet_number IS NOT NULL",
"pallet_number != ''",
"date_reserved IS NOT NULL",
"customer_number IS NOT NULL",
"customer_number != ''"
]
junk = ["", "0", "0000", "N/A", "TBD", "null", "999990", "999995"]
junk_list = "', '".join(junk)
conditions.append("pallet_number NOT IN ('" + junk_list + "')")
conditions.append("pallet_number NOT LIKE '999%'")
conditions.append("pallet_number REGEXP '^[0-9]'")
if status_filter:
conditions.append("status = '" + frappe.db.escape(status_filter) + "'")
if search:
conditions.append("pallet_number LIKE '%" + frappe.db.escape(search) + "%'")
where_clause = " AND ".join(conditions)
total = frappe.db.sql("SELECT COUNT(*) FROM tabPallet WHERE " + where_clause)[0][0]
pallets = frappe.db.sql("SELECT 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 FROM tabPallet WHERE " + where_clause + " ORDER BY CAST(pallet_number AS UNSIGNED) " + sort_dir + " LIMIT " + str(page_size) + " OFFSET " + str(offset), as_dict=True)
empty_pallet = frappe.db.sql("SELECT 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 FROM tabPallet WHERE date_reserved IS NULL AND (customer_number IS NULL OR customer_number = '') AND pallet_number NOT IN ('" + junk_list + "') AND pallet_number NOT LIKE '999%' AND pallet_number REGEXP '^[0-9]' ORDER BY CAST(pallet_number AS UNSIGNED) DESC LIMIT 1", as_dict=True)
result = []
if empty_pallet:
empty_pallet[0]["_is_new"] = True
result.append(empty_pallet[0])
result.extend(pallets)
return {"pallets": result, "total": total, "page": page, "page_size": page_size}
@frappe.whitelist()
def update_pallet(docname, field, value):
pallet = frappe.get_doc("Pallet", docname)
pallet.set(field, value)
pallet.save(ignore_permissions=True)
frappe.db.commit()
return {"status": "ok", "message": "Updated " + field}
@frappe.whitelist()
def create_pallet(data):
data = frappe.parse_json(data)
pallet = frappe.new_doc("Pallet")
pallet.pallet_number = data.get("pallet_number")
pallet.status = data.get("status", "Received")
pallet.date_reserved = data.get("date_reserved")
for field, value in data.items():
if field not in ["pallet_number", "status", "date_reserved"] and value:
pallet.set(field, value)
pallet.insert(ignore_permissions=True)
frappe.db.commit()
return {"status": "ok", "name": pallet.name}
+7 -7
View File
@@ -1,6 +1,6 @@
<style>
.pallet-list-page { font-family: Helvetica Neue, Arial, sans-serif; }
.pallet-list-page h2 { color: #2F5496; margin-bottom: 16px; font-size: 20px; }
.pallet-list-page h2 { color: #6f42c1; margin-bottom: 16px; font-size: 20px; }
.pallet-search-box { margin-bottom: 16px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.pallet-search-box input, .pallet-search-box select {
padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px;
@@ -9,18 +9,18 @@
padding: 6px 14px; border: 1px solid #ddd; border-radius: 4px; cursor: pointer;
font-size: 13px; background: white;
}
.pallet-search-box button.btn-primary { background: #2F5496; color: white; border-color: #2F5496; }
.pallet-search-box button.btn-success { background: #548235; color: white; border-color: #548235; }
.pallet-search-box button.btn-primary { background: #6f42c1; color: white; border-color: #6f42c1; }
.pallet-search-box button.btn-success { background: #28a745; color: white; border-color: #28a745; }
.pallet-table-container { padding: 0; overflow-x: auto; background: white; border-radius: 4px; }
.pallet-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.pallet-table th {
background: #2F5496; color: white; padding: 8px 10px; text-align: left;
background: #6f42c1; color: white; padding: 8px 10px; text-align: left;
cursor: pointer; white-space: nowrap; font-weight: 500;
}
.pallet-table td { padding: 5px 10px; border-bottom: 1px solid #eee; }
.pallet-table tr:hover { background: #f5f5f5; }
.pallet-table tr.new-row { background: #FFF9E6 !important; }
.pallet-table a { color: #2F5496; text-decoration: none; font-weight: 600; }
.pallet-table a { color: #6f42c1; text-decoration: none; font-weight: 600; }
.status-received { color: #2196F3; font-weight: 600; }
.status-sorting { color: #FF9800; font-weight: 600; }
.status-processing { color: #9C27B0; font-weight: 600; }
@@ -29,9 +29,9 @@
.pallet-pagination { margin-top: 12px; display: flex; gap: 5px; align-items: center; }
.pallet-pagination button {
padding: 5px 12px; border: 1px solid #ddd; background: white; cursor: pointer;
border-radius: 4px; font-size: 13px; color: #2F5496;
border-radius: 4px; font-size: 13px; color: #6f42c1;
}
.pallet-pagination button.active { background: #2F5496; color: white; border-color: #2F5496; }
.pallet-pagination button.active { background: #6f42c1; color: white; border-color: #6f42c1; }
.pallet-pagination button:disabled { opacity: 0.5; cursor: not-allowed; }
.pallet-count { margin-left: auto; color: #666; font-size: 13px; }
</style>
+1 -1
View File
@@ -17,7 +17,7 @@ frappe.pages["pallet-list"].on_page_load = function(wrapper) {
function loadPallets() {
frappe.call({
method: "westech_r2.page.pallet-list.pallet-list.get_pallets",
method: "westech_r2.page.pallet_list.pallet_list.get_pallets",
args: {
page: currentPage,
page_size: pageSize,
+17 -17
View File
@@ -1,19 +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
"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"
}