frappe.pages['receiving'].on_page_load = function(wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, title: 'Receiving', single_column: true }); // Inline HTML — same pattern as intake.js $(wrapper).find('.layout-main-section').html(`

🚛 Receiving

Schedule pickups, manage routes, and check in loads.

📅 Pickup Calendar — Next 30 Days
Loading...
📊 Weekly Pickup Volume
Scheduled Pickups
DateWeekdayTypeCustomerContactAddressEst. ItemsDataRED/R2StatusNotesTruckAoRCoD
Loading...
🚛 Truck 1
🚛 Truck 2
🚛 Truck 3
📋 Unassigned
Recent Check-ins
DateCustomerTypeActual PalletsActual WeightLoad ContentsData StatusRED/R2Status
Loading...
`); // ── Stage Tabs ── $("#receiving-tabs a").on("click", function(e) { e.preventDefault(); $(this).tab("show"); var stage = $(this).attr("href").replace("#stage-", ""); if (stage === "a") loadPickups(); if (stage === "b") loadRoutes(); if (stage === "c") loadCheckins(); }); // ── 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 customer...", 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(); } 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 || ""); $("#sp-legacy_notes").val(c.legacy_notes || ""); $("#sp-hours_of_operation").val(c.hours_of_operation || ""); 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, #sp-legacy_notes, #sp-hours_of_operation").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 }, callback: function(r) { if (r.message) { renderPickupTable(r.message.pickups || []); renderCalendar(r.message.calendar || []); renderWeeklyChart(r.message.weekly || []); $("#pickup-count-label").text((r.message.pickups || []).length + " pickups"); } } }); } function renderPickupTable(pickups) { var tbody = $("#pickup-tbody"); if (!pickups.length) { tbody.html('No scheduled pickups'); return; } var statusColors = { "Scheduled": "#2196F3", "Routed": "#009688", "In Progress": "#FF9800", "Complete": "#4CAF50", "Cancelled": "#F44336" }; var h = ""; pickups.forEach(function(p) { var st = p.status || "Scheduled"; var sc = statusColors[st] || "#999"; var weekday = p.pickup_date ? dayName(new Date(p.pickup_date + "T12:00:00")) : ""; var typeBadge = p.pickup_type === "Drop-off" ? 'Drop-off' : 'Pickup'; h += ''; h += '' + esc(p.pickup_date || "") + ''; h += '' + weekday + ''; h += '' + typeBadge + ''; h += '' + esc(p.company_name || p.customer_number || "") + ''; h += '' + esc((p.contact_name || "") + (p.contact_phone ? " • " + p.contact_phone : "")) + ''; h += '' + esc((p.address_line || "") + (p.city ? ", " + p.city : "")) + ''; h += '' + (p.estimated_items || "—") + ''; h += '' + esc(p.data_status || "—") + ''; h += '' + esc(p.red_r2 || "—") + ''; h += '' + esc(st) + ''; h += '' + esc(p.notes || "") + ''; h += '' + esc(p.truck || "—") + ''; h += '' + (p.needs_aor ? "✓" : "") + ''; h += '' + (p.needs_cod ? "✓" : "") + ''; h += ''; }); tbody.html(h); } function renderCalendar(days) { var el = $("#pickup-calendar"); if (!days || !days.length) { el.html('
No upcoming pickups
'); return; } var today = frappe.datetime.nowdate(); var h = '
'; ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].forEach(function(d) { h += '
' + d + '
'; }); var first = new Date(days[0].date + "T12:00:00"); for (var i = 0; i < first.getDay(); i++) h += '
'; days.forEach(function(d) { var isToday = d.date === today; var hasCount = d.count > 0; var bg = isToday ? "cal-day today" : (hasCount ? "cal-day has-pickups" : "cal-day"); var onclick = hasCount ? "onclick=$('#pickup-date-filter').val('" + d.date + "');loadPickups();" : ""; h += '
'; h += '
' + d.date.split("-")[2] + '
'; if (hasCount) h += '
' + d.count + '
'; h += '
'; }); h += '
'; el.html(h); } function renderWeeklyChart(weekly) { var canvas = document.getElementById("weekly-chart"); if (!canvas || !weekly || !weekly.length) return; var ctx = canvas.getContext("2d"); var W = canvas.width = canvas.parentElement.clientWidth - 20; var H = canvas.height = 180; ctx.clearRect(0, 0, W, H); var maxVal = Math.max.apply(null, weekly.map(function(w) { return w.count; })); if (maxVal < 1) maxVal = 1; var barW = Math.min(50, (W - 40) / weekly.length - 8); var startX = 40; var barArea = H - 40; weekly.forEach(function(w, i) { var x = startX + i * ((W - 60) / weekly.length); var barH = (w.count / maxVal) * barArea; var y = H - 30 - barH; ctx.fillStyle = "#2F5496"; ctx.fillRect(x, y, barW, barH); ctx.fillStyle = "#333"; ctx.font = "11px sans-serif"; ctx.textAlign = "center"; ctx.fillText(w.count, x + barW / 2, y - 4); ctx.fillStyle = "#999"; ctx.font = "10px sans-serif"; ctx.fillText(w.label, x + barW / 2, H - 10); }); } // ── 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(); if (customer_control) customer_control.set_value(""); }); $("#pickup-form").on("submit", function(e) { e.preventDefault(); var doc = { doctype: "Scheduled Pickup", pickup_date: $("#sp-pickup_date").val(), pickup_type: $("#sp-pickup_type").val(), customer_number: customer_control ? customer_control.get_value() : "", 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(), legacy_notes: $("#sp-legacy_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 + " stops)"); $("#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; 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) { return '' + esc(c.pickup_date || "") + '' + '' + esc(c.company_name || "") + '' + '' + esc(c.pickup_type || "") + '' + '' + (c.estimated_items || "—") + '' + '' + (c.estimated_weight || "—") + '' + '' + esc(c.load_contents || "") + '' + '' + esc(c.data_status || "—") + '' + '' + esc(c.red_r2 || "—") + '' + '' + esc(c.status || "") + ''; }).join("")); } $("#btn-new-checkin").on("click", function() { $("#checkin-form").show(); $("#ci-received_date").val(frappe.datetime.nowdate()); 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"]] ] }; } }, only_input: true, }); checkin_pickup_control.refresh(); $("#ci-pickup-control .control-input").css("margin", "0"); $("#ci-pickup-control .help-box").remove(); }); $("#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 update = {}; update.status = "Complete"; if ($("#ci-actual_pallets").val()) update.estimated_items = parseInt($("#ci-actual_pallets").val()); if ($("#ci-actual_weight").val()) update.estimated_weight = $("#ci-actual_weight").val(); if ($("#ci-load_contents").val()) update.load_contents = $("#ci-load_contents").val(); frappe.call({ method: "frappe.client.set_value", args: { doctype: "Scheduled Pickup", name: pickupName, fieldname: update }, callback: function(r) { if (r.message) { frappe.show_alert({ message: "Load checked in", indicator: "green" }); $("#checkin-form").hide(); loadCheckins(); } } }); }); $("#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(); };