frappe.pages['receiving'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Receiving',
single_column: true
});
// Inline HTML
$(wrapper).find('.layout-main-section').html(`
🚛 Receiving
Schedule pickups, manage routes, and check in loads.
📅 Pickup Calendar — Next 30 Days
Date Weekday Type Customer Contact Address Est. Items Data RED/R2 Status Notes Truck AoR CoD
Loading...
+ Check In Load 📋 CoR Report
Recent Check-ins
Date Customer Load # Pallets Weight Contents Data Status RED/R2
Loading...
`);
// Prevent Frappe router from intercepting tab clicks
$("#receiving-tabs").on("click", "a[data-toggle=tab]", function(e) {
e.preventDefault();
$(this).tab("show");
});
// ── Stage A: Link Controls ──
var customer_control = null;
function setupCustomerLink() {
customer_control = frappe.ui.form.make_control({
parent: $("#sp-customer-control"),
df: {
fieldtype: "Link",
fieldname: "customer_number",
options: "Customer",
label: "Customer",
reqd: 1,
placeholder: "Search by name, number, or address...",
onchange: function() {
var val = customer_control.get_value();
if (val) fetchCustomerDetails(val);
else clearCustomerFields();
}
},
only_input: true,
});
customer_control.refresh();
$("#sp-customer-control .control-input").css("margin", "0");
$("#sp-customer-control .help-box").remove();
}
// New Customer button
$(document).on("click", "#btn-new-customer", function() {
frappe.call({
method: "frappe.client.insert",
args: { doc: { doctype: "Customer", customer_type: "Company", customer_group: "All Customer Groups", territory: "United States" } },
callback: function(r) {
if (r.message) {
frappe.set_route("Form", "Customer", r.message.name);
}
}
});
});
function fetchCustomerDetails(customer_name) {
frappe.call({
method: "frappe.client.get",
args: { doctype: "Customer", name: customer_name },
callback: function(r) {
if (!r.message) return;
var c = r.message;
$("#sp-company_name").val(c.customer_name || "");
$("#sp-contact_name").val(c.contact_name || "");
$("#sp-contact_phone").val(c.contact_phone || "");
$("#sp-contact_email").val(c.contact_email || "");
frappe.call({
method: "frappe.client.get_list",
args: {
doctype: "Address",
filters: [["Dynamic Link", "link_name", "=", customer_name]],
fields: ["address_line1", "city", "state", "pincode"],
limit_page_length: 1
},
callback: function(ra) {
if (ra.message && ra.message.length) {
var a = ra.message[0];
$("#sp-address_line").val(a.address_line1 || "");
$("#sp-city").val(a.city || "");
$("#sp-state").val(a.state || "AZ");
$("#sp-zip_code").val(a.pincode || "");
}
}
});
frappe.call({
method: "frappe.client.get_list",
args: {
doctype: "Contact",
filters: [["Dynamic Link", "link_name", "=", customer_name]],
fields: ["first_name", "last_name", "email_id", "phone", "mobile_no"],
limit_page_length: 1
},
callback: function(rc) {
if (rc.message && rc.message.length) {
var ct = rc.message[0];
if (!$("#sp-contact_name").val()) {
$("#sp-contact_name").val((ct.first_name || "") + " " + (ct.last_name || ""));
}
if (!$("#sp-contact_phone").val()) {
$("#sp-contact_phone").val(ct.phone || ct.mobile_no || "");
}
if (!$("#sp-contact_email").val()) {
$("#sp-contact_email").val(ct.email_id || "");
}
}
}
});
}
});
}
function clearCustomerFields() {
$("#sp-company_name, #sp-contact_name, #sp-contact_phone, #sp-contact_email, #sp-address_line, #sp-city, #sp-state, #sp-zip_code").val("");
$("#sp-state").val("AZ");
}
// ── Stage A: Load Pickups ──
function loadPickups() {
var dateFilter = $("#pickup-date-filter").val();
frappe.call({
method: "westech_r2.api.receiving_api.get_pickups",
args: { date: dateFilter || undefined },
callback: function(r) {
if (r.message) {
renderPickupTable(r.message.pickups || []);
renderCalendar(r.message.calendar || []);
}
}
});
}
function renderPickupTable(pickups) {
var tbody = $("#pickup-tbody");
$("#pickup-count-label").text("(" + pickups.length + ")");
if (!pickups.length) {
tbody.html('No pickups found ');
return;
}
tbody.html(pickups.map(function(p) {
var dt = p.pickup_date ? new Date(p.pickup_date + "T00:00:00") : null;
var dn = dt ? dayName(dt) : "";
return '' +
'' + esc(p.pickup_date || "") + ' ' +
'' + dn + ' ' +
'' + esc(p.pickup_type || "") + ' ' +
'' + esc(p.company_name || p.customer_number || "") + ' ' +
'' + esc(p.contact_name || "") + ' ' +
'' + esc((p.address_line || "") + (p.city ? ", " + p.city : "")) + ' ' +
'' + (p.estimated_items || "—") + ' ' +
'' + esc(p.data_status || "—") + ' ' +
'' + esc(p.red_r2 || "—") + ' ' +
'' + esc(p.status || "") + ' ' +
'' + esc(p.notes || "") + ' ' +
'' + esc(p.truck || "") + ' ' +
'' + (p.needs_aor ? "✓" : "") + ' ' +
'' + (p.needs_cod ? "✓" : "") + ' ';
}).join(""));
}
function renderCalendar(calendar) {
var el = $("#pickup-calendar");
if (!calendar.length) { el.html('No data
'); return; }
var todayStr = frappe.datetime.nowdate();
var html = '';
calendar.forEach(function(d) {
var cls = "cal-day";
if (d.count > 0) cls += " has-pickups";
if (d.date === todayStr) cls += " today";
var label = d.date.substring(5);
html += '
' + label + '
';
});
html += '
';
el.html(html);
}
$(document).on("click", ".cal-day.has-pickups", function() {
$("#pickup-date-filter").val($(this).data("date"));
loadPickups();
});
// ── Stage A: New Pickup ──
$("#btn-new-pickup").on("click", function() {
$("#new-pickup-form").show();
$("#sp-pickup_date").val(frappe.datetime.nowdate());
setupCustomerLink();
});
$("#btn-cancel-pickup").on("click", function() {
$("#new-pickup-form").hide();
});
$("#pickup-form").on("submit", function(e) {
e.preventDefault();
var customerVal = customer_control ? customer_control.get_value() : "";
if (!customerVal) { frappe.msgprint("Select a customer"); return; }
var doc = {
doctype: "Scheduled Pickup",
pickup_date: $("#sp-pickup_date").val(),
pickup_type: $("#sp-pickup_type").val(),
customer_number: customerVal,
company_name: $("#sp-company_name").val(),
contact_name: $("#sp-contact_name").val(),
contact_phone: $("#sp-contact_phone").val(),
contact_email: $("#sp-contact_email").val(),
address_line: $("#sp-address_line").val(),
city: $("#sp-city").val(),
state: $("#sp-state").val(),
zip_code: $("#sp-zip_code").val(),
estimated_items: parseInt($("#sp-estimated_items").val()) || 0,
estimated_weight: $("#sp-estimated_weight").val(),
load_contents: $("#sp-load_contents").val(),
data_status: $("#sp-data_status").val(),
red_r2: $("#sp-red_r2").val(),
needs_aor: $("#sp-needs_aor").is(":checked") ? 1 : 0,
needs_cod: $("#sp-needs_cod").is(":checked") ? 1 : 0,
notes: $("#sp-notes").val(),
status: "Scheduled"
};
frappe.call({
method: "frappe.client.insert",
args: { doc: doc },
callback: function(r) {
if (r.message) {
frappe.show_alert({ message: "Pickup scheduled", indicator: "green" });
$("#new-pickup-form").hide();
loadPickups();
}
}
});
});
$("#pickup-date-filter").on("change", loadPickups);
$("#btn-clear-date").on("click", function() {
$("#pickup-date-filter").val("");
loadPickups();
});
// ── Stage B: Routing ──
function loadRoutes() {
var date = $("#route-date").val() || frappe.datetime.nowdate();
$("#route-date").val(date);
frappe.call({
method: "westech_r2.api.receiving_api.get_pickups",
args: { date: date },
callback: function(r) {
if (r.message) renderRouteColumns(r.message.pickups || []);
}
});
}
function renderRouteColumns(pickups) {
var trucks = { "Truck 1": [], "Truck 2": [], "Truck 3": [], "Unassigned": [] };
pickups.forEach(function(p) {
var t = p.truck || "";
if (t && trucks[t]) trucks[t].push(p);
else trucks["Unassigned"].push(p);
});
["Truck 1", "Truck 2", "Truck 3"].forEach(function(t) {
var key = t.toLowerCase().replace(/ /g, "");
$("#" + key + "-count").text("(" + trucks[t].length + " stops)");
$("#" + key + "-stops").html(trucks[t].map(function(p, i) { return stopCard(p, i + 1); }).join(""));
});
$("#unassigned-count").text("(" + trucks["Unassigned"].length + ")");
$("#unassigned-stops").html(trucks["Unassigned"].map(function(p) { return stopCard(p, 0); }).join(""));
}
function stopCard(p, order) {
var h = '';
if (order) h += '
Stop #' + order + '
';
h += '
' + esc(p.company_name || p.customer_number || "Unknown") + '
';
h += '
' + esc((p.address_line || "") + (p.city ? ", " + p.city : "")) + '
';
h += '
';
if (p.estimated_items) h += '' + p.estimated_items + ' items ';
if (p.data_status) h += '' + esc(p.data_status) + ' ';
if (p.red_r2) h += '' + esc(p.red_r2) + ' ';
if (p.needs_aor) h += 'AoR ';
if (p.needs_cod) h += 'CoD ';
h += '
';
return h;
}
$("#btn-load-routes").on("click", loadRoutes);
$("#btn-auto-route").on("click", function() {
var date = $("#route-date").val();
if (!date) { frappe.msgprint("Select a date first"); return; }
frappe.call({
method: "westech_r2.api.receiving_api.auto_route",
args: { date: date },
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({ message: "Routes optimized", indicator: "green" });
loadRoutes();
}
}
});
});
$("#btn-route-sheet").on("click", function() {
var date = $("#route-date").val() || frappe.datetime.nowdate();
window.open("/api/method/westech_r2.api.receiving_api.print_route_sheet?date=" + date, "_blank");
});
$("#btn-green-sheet").on("click", function() {
var date = $("#route-date").val() || frappe.datetime.nowdate();
window.open("/api/method/westech_r2.api.receiving_api.print_green_sheet?date=" + date, "_blank");
});
$("#btn-labels").on("click", function() {
var date = $("#route-date").val() || frappe.datetime.nowdate();
window.open("/api/method/westech_r2.api.receiving_api.print_labels?date=" + date, "_blank");
});
// ── Stage C: Check-in ──
var checkin_pickup_control = null;
var current_pickup_details = null;
function loadCheckins() {
frappe.call({
method: "westech_r2.api.receiving_api.get_checkins",
callback: function(r) {
if (r.message) renderCheckinTable(r.message.checkins || []);
}
});
}
function renderCheckinTable(checkins) {
var tbody = $("#checkin-tbody");
if (!checkins.length) {
tbody.html('No check-ins yet ');
return;
}
tbody.html(checkins.map(function(c) {
var palletInfo = (c.pallets || []).map(function(p) { return esc(p.pallet_number || p.name); }).join(", ");
return '' +
'' + esc(c.incoming_date || "") + ' ' +
'' + esc(c.customer_name || c.customer || "") + ' ' +
'' + esc(c.name || "") + ' ' +
'' + (c.pallet_count || 0) + ' ' +
'' + (c.total_weight || "—") + ' ' +
'' + esc(c.data_status || "") + ' ' +
'' + esc(c.red_r2 || "—") + ' ' +
'' + palletInfo + ' ';
}).join(""));
}
$("#btn-new-checkin").on("click", function() {
$("#checkin-form").show();
$("#ci-received_date").val(frappe.datetime.nowdate());
$("#ci-pickup-details").hide();
current_pickup_details = null;
checkin_pickup_control = frappe.ui.form.make_control({
parent: $("#ci-pickup-control"),
df: {
fieldtype: "Link",
fieldname: "pickup_ref",
options: "Scheduled Pickup",
label: "Scheduled Pickup",
reqd: 1,
placeholder: "Search pickup...",
get_query: function() {
return {
filters: [
["Scheduled Pickup", "status", "in", ["Scheduled", "Routed", "In Progress"]]
]
};
},
onchange: function() {
var val = checkin_pickup_control ? checkin_pickup_control.get_value() : "";
if (val) loadPickupDetails(val);
else {
$("#ci-pickup-details").hide();
current_pickup_details = null;
}
}
},
only_input: true,
});
checkin_pickup_control.refresh();
$("#ci-pickup-control .control-input").css("margin", "0");
$("#ci-pickup-control .help-box").remove();
});
function loadPickupDetails(pickup_name) {
frappe.call({
method: "westech_r2.api.receiving_api.get_pickup_details",
args: { pickup_name: pickup_name },
callback: function(r) {
if (!r.message) return;
current_pickup_details = r.message;
var d = r.message;
$("#ci-pickup-details").show();
$("#ci-customer-info").text((d.company_name || d.customer_number || "Unknown") + (d.red_r2 ? " — " + d.red_r2 : ""));
$("#ci-address-info").text((d.address_line || "") + (d.city ? ", " + d.city : "") + (d.state ? ", " + d.state : "") + (d.zip_code ? " " + d.zip_code : ""));
$("#ci-contact-info").text((d.contact_name || "") + (d.contact_phone ? " • " + d.contact_phone : "") + (d.contact_email ? " • " + d.contact_email : ""));
// Special handling for RED/NIST
if (d.red_r2 && d.red_r2 !== "Neither" && d.red_r2 !== "") {
$("#ci-special-handling").show().html("⚠ " + esc(d.red_r2) + " — Special handling required" + (d.needs_aor ? " • AoR ✓" : "") + (d.needs_cod ? " • CoD ✓" : ""));
} else {
$("#ci-special-handling").hide();
}
// Notes
if (d.notes) {
$("#ci-pickup-notes").show().html("Notes: " + esc(d.notes));
} else {
$("#ci-pickup-notes").hide();
}
// Pre-fill check-in fields from pickup
$("#ci-actual_pallets").val(d.estimated_items || 1);
$("#ci-data_status").val(d.data_status || "");
$("#ci-red_r2").val(d.red_r2 || "");
}
});
}
$("#btn-cancel-checkin").on("click", function() {
$("#checkin-form").hide();
});
$("#checkin-form-inner").on("submit", function(e) {
e.preventDefault();
var pickupName = checkin_pickup_control ? checkin_pickup_control.get_value() : "";
if (!pickupName) { frappe.msgprint("Select a pickup"); return; }
var actualPallets = parseInt($("#ci-actual_pallets").val()) || 0;
if (actualPallets < 1) { frappe.msgprint("Enter at least 1 pallet"); return; }
frappe.confirm(
"Check in " + actualPallets + " pallet(s) for this load?",
function() {
frappe.call({
method: "westech_r2.api.receiving_api.checkin_load",
args: {
pickup_name: pickupName,
received_date: $("#ci-received_date").val(),
actual_pallets: actualPallets,
total_weight: $("#ci-total_weight").val(),
load_contents: $("#ci-load_contents").val(),
data_status: $("#ci-data_status").val(),
red_r2: $("#ci-red_r2").val()
},
callback: function(r) {
if (r.message && r.message.success) {
frappe.show_alert({
message: "Load checked in! Created " + r.message.pallets_created + " pallet(s) in Load " + r.message.load,
indicator: "green"
});
$("#checkin-form").hide();
loadCheckins();
loadPickups();
}
}
});
}
);
});
$("#btn-cor-report").on("click", function() {
window.open("/api/method/westech_r2.api.receiving_api.cor_report", "_blank");
});
// ── Helpers ──
function esc(s) { return s ? String(s).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """) : ""; }
function dayName(d) { return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][d.getDay()]; }
// ── Init ──
loadPickups();
loadCheckins();
};