567 lines
33 KiB
JavaScript
567 lines
33 KiB
JavaScript
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(`
|
|
<div class="receiving-station" style="padding: 20px;">
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<h3 style="margin-top: 0; color: #2F5496;">🚛 Receiving</h3>
|
|
<p class="text-muted">Schedule pickups, manage routes, and check in loads.</p>
|
|
</div>
|
|
</div>
|
|
<ul class="nav nav-tabs" role="tablist" id="receiving-tabs">
|
|
<li role="presentation" class="active"><a href="#stage-a" role="tab" data-toggle="tab">📋 Stage A — Schedule Pickup</a></li>
|
|
<li role="presentation"><a href="#stage-b" role="tab" data-toggle="tab">🗺️ Stage B — Route & Dispatch</a></li>
|
|
<li role="presentation"><a href="#stage-c" role="tab" data-toggle="tab">⚖️ Stage C — Load Check-in</a></li>
|
|
</ul>
|
|
<div class="tab-content" style="padding-top: 20px;">
|
|
<div role="tabpanel" class="tab-pane active" id="stage-a">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="panel panel-primary">
|
|
<div class="panel-heading">📅 Pickup Calendar — Next 30 Days</div>
|
|
<div class="panel-body" id="pickup-calendar"><div class="text-muted text-center">Loading...</div></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-9">
|
|
<div class="panel panel-default">
|
|
<div class="panel-heading">
|
|
<div class="row">
|
|
<div class="col-md-6"><strong>Scheduled Pickups</strong><span id="pickup-count-label" class="text-muted" style="margin-left: 8px;"></span></div>
|
|
<div class="col-md-6 text-right">
|
|
<button class="btn btn-primary btn-sm" id="btn-new-pickup">+ New Pickup</button>
|
|
<input type="date" id="pickup-date-filter" class="form-control input-sm" style="display:inline-block;width:auto;vertical-align:middle;margin-left:8px;">
|
|
<button class="btn btn-default btn-sm" id="btn-clear-date" style="margin-left:4px;">Clear</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body" style="padding: 0; overflow-x: auto;">
|
|
<table class="table table-striped table-hover" id="pickup-table" style="font-size: 13px; margin-bottom: 0;">
|
|
<thead><tr><th>Date</th><th>Weekday</th><th>Type</th><th>Customer</th><th>Contact</th><th>Address</th><th>Est. Items</th><th>Data</th><th>RED/R2</th><th>Status</th><th>Notes</th><th>Truck</th><th>AoR</th><th>CoD</th></tr></thead>
|
|
<tbody id="pickup-tbody"><tr><td colspan="14" class="text-center text-muted">Loading...</td></tr></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="new-pickup-form" style="display:none; margin-top: 16px;">
|
|
<div class="panel panel-primary">
|
|
<div class="panel-heading">+ New Scheduled Pickup</div>
|
|
<div class="panel-body">
|
|
<form id="pickup-form">
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<h5 style="color:#6f42c1;">📅 Pickup Info</h5>
|
|
<div class="form-group"><label>Pickup Date <span class="text-danger">*</span></label><input type="date" id="sp-pickup_date" class="form-control" required></div>
|
|
<div class="form-group"><label>Type <span class="text-danger">*</span></label><select id="sp-pickup_type" class="form-control" required><option value="Pickup">Pickup</option><option value="Drop-off">Drop-off</option></select></div>
|
|
<div class="form-group"><label>Customer <span class="text-danger">*</span></label><div id="sp-customer-control"></div></div>
|
|
<div class="form-group"><label>Company Name</label><input type="text" id="sp-company_name" class="form-control" readonly style="background:#f8f9fa;"></div>
|
|
<div class="form-group"><label>Contact Name</label><input type="text" id="sp-contact_name" class="form-control"></div>
|
|
<div class="form-group"><label>Contact Phone</label><input type="text" id="sp-contact_phone" class="form-control"></div>
|
|
<div class="form-group"><label>Contact Email</label><input type="email" id="sp-contact_email" class="form-control"></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<h5 style="color:#6f42c1;">📍 Address</h5>
|
|
<div class="form-group"><label>Street Address</label><input type="text" id="sp-address_line" class="form-control"></div>
|
|
<div class="form-group"><label>City</label><input type="text" id="sp-city" class="form-control"></div>
|
|
<div class="form-group"><label>State</label><input type="text" id="sp-state" class="form-control" value="AZ"></div>
|
|
<div class="form-group"><label>ZIP</label><input type="text" id="sp-zip_code" class="form-control"></div>
|
|
<div class="form-group"><label>Hours of Operation</label><input type="text" id="sp-hours_of_operation" class="form-control" placeholder="e.g. Mon-Fri 8am-5pm"></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<h5 style="color:#6f42c1;">📦 Load Info</h5>
|
|
<div class="form-group"><label>Estimated Items</label><input type="number" id="sp-estimated_items" class="form-control"></div>
|
|
<div class="form-group"><label>Estimated Weight</label><input type="text" id="sp-estimated_weight" class="form-control"></div>
|
|
<div class="form-group"><label>Load Contents</label><input type="text" id="sp-load_contents" class="form-control" placeholder="Wire, Monitors, Laptops..."></div>
|
|
<div class="form-group"><label>Data Status</label><select id="sp-data_status" class="form-control"><option value="">—</option><option value="D0">D0</option><option value="D1">D1</option><option value="ND1">ND1</option><option value="ND2">ND2</option><option value="ND3">ND3</option><option value="ND4">ND4</option></select></div>
|
|
<div class="form-group"><label>RED / R2</label><select id="sp-red_r2" class="form-control"><option value="">—</option><option value="RED">RED</option><option value="R2">R2</option><option value="Both">Both</option><option value="Neither">Neither</option></select></div>
|
|
<div class="form-group"><div class="checkbox"><label><input type="checkbox" id="sp-needs_aor"> <strong>Needs AoR</strong></label></div><div class="checkbox"><label><input type="checkbox" id="sp-needs_cod"> <strong>Needs CoD</strong></label></div></div>
|
|
<div class="form-group"><label>Notes</label><textarea id="sp-notes" class="form-control" rows="2"></textarea></div>
|
|
<div class="form-group"><label>Legacy Notes</label><textarea id="sp-legacy_notes" class="form-control" rows="2" style="background:#fafafa;" readonly></textarea></div>
|
|
</div>
|
|
</div>
|
|
<div class="row" style="margin-top: 16px;"><div class="col-md-12"><button type="submit" class="btn btn-primary btn-lg">Save Pickup</button><button type="button" class="btn btn-default btn-lg" id="btn-cancel-pickup">Cancel</button></div></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div role="tabpanel" class="tab-pane" id="stage-b">
|
|
<div class="row" style="margin-bottom: 16px;"><div class="col-md-12"><div class="btn-group"><input type="date" id="route-date" class="form-control" style="display:inline-block;width:auto;"><button class="btn btn-primary" id="btn-load-routes">Load Pickups</button><button class="btn btn-primary" id="btn-auto-route">🧮 Auto-Route</button><button class="btn btn-success" id="btn-route-sheet">🖨️ Route Sheet</button><button class="btn btn-success" id="btn-green-sheet">📄 Green Sheet</button><button class="btn btn-success" id="btn-labels">🏷️ Labels</button></div></div></div>
|
|
<div class="row" id="route-columns">
|
|
<div class="col-md-4"><div class="panel panel-default truck-column" data-truck="Truck 1"><div class="panel-heading">🚛 Truck 1 <span id="truck1-count" class="text-muted"></span></div><div class="panel-body truck-stops" id="truck1-stops"></div></div></div>
|
|
<div class="col-md-4"><div class="panel panel-default truck-column" data-truck="Truck 2"><div class="panel-heading">🚛 Truck 2 <span id="truck2-count" class="text-muted"></span></div><div class="panel-body truck-stops" id="truck2-stops"></div></div></div>
|
|
<div class="col-md-4"><div class="panel panel-default truck-column" data-truck="Truck 3"><div class="panel-heading">🚛 Truck 3 <span id="truck3-count" class="text-muted"></span></div><div class="panel-body truck-stops" id="truck3-stops"></div></div></div>
|
|
</div>
|
|
<div class="row" style="margin-top: 16px;"><div class="col-md-12"><div class="panel panel-default truck-column" data-truck=""><div class="panel-heading">📋 Unassigned <span id="unassigned-count" class="text-muted"></span></div><div class="panel-body truck-stops" id="unassigned-stops"></div></div></div></div>
|
|
</div>
|
|
<div role="tabpanel" class="tab-pane" id="stage-c">
|
|
<div class="row" style="margin-bottom: 16px;"><div class="col-md-12"><button class="btn btn-primary" id="btn-new-checkin">+ Check In Load</button><button class="btn btn-success" id="btn-cor-report">📋 CoR Report</button></div></div>
|
|
<div class="panel panel-default">
|
|
<div class="panel-heading">Recent Check-ins</div>
|
|
<div class="panel-body" style="padding: 0; overflow-x: auto;">
|
|
<table class="table table-striped table-hover" id="checkin-table" style="font-size: 13px; margin-bottom: 0;">
|
|
<thead><tr><th>Date</th><th>Customer</th><th>Type</th><th class="text-right">Actual Pallets</th><th class="text-right">Actual Weight</th><th>Load Contents</th><th>Data Status</th><th>RED/R2</th><th>Status</th></tr></thead>
|
|
<tbody id="checkin-tbody"><tr><td colspan="9" class="text-center text-muted">Loading...</td></tr></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div id="checkin-form" style="display:none; margin-top: 16px;">
|
|
<div class="panel panel-primary">
|
|
<div class="panel-heading">+ Load Check-in</div>
|
|
<div class="panel-body">
|
|
<form id="checkin-form-inner">
|
|
<div class="row">
|
|
<div class="col-md-4"><div class="form-group"><label>Scheduled Pickup <span class="text-danger">*</span></label><div id="ci-pickup-control"></div></div></div>
|
|
<div class="col-md-4"><div class="form-group"><label>Received Date <span class="text-danger">*</span></label><input type="date" id="ci-received_date" class="form-control" required></div></div>
|
|
<div class="col-md-4"><div class="form-group"><label>Actual # of Pallets/Gaylords</label><input type="number" id="ci-actual_pallets" class="form-control"></div></div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-4"><div class="form-group"><label>Actual Weight (lbs)</label><input type="text" id="ci-actual_weight" class="form-control"></div></div>
|
|
<div class="col-md-8"><div class="form-group"><label>Load Contents</label><textarea id="ci-load_contents" class="form-control" rows="2" placeholder="Wire, Monitors, Laptops, MRI machine..."></textarea></div></div>
|
|
</div>
|
|
<div class="row" style="margin-top: 8px;"><div class="col-md-12"><button type="submit" class="btn btn-primary btn-lg">Check In</button><button type="button" class="btn btn-default btn-lg" id="btn-cancel-checkin">Cancel</button></div></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<style>
|
|
.truck-stops { min-height: 60px; }
|
|
.stop-card { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 6px; padding: 10px 12px; margin: 6px 0; cursor: grab; font-size: 13px; }
|
|
.stop-card:hover { border-color: #2F5496; }
|
|
.stop-card .stop-co { font-weight: 700; color: #2F5496; }
|
|
.stop-card .stop-addr { color: #666; font-size: 12px; margin-top: 2px; }
|
|
.stop-card .stop-meta { display: flex; gap: 8px; margin-top: 4px; font-size: 11px; }
|
|
.stop-card .stop-meta span { background: #D6E4F0; color: #2F5496; padding: 1px 6px; border-radius: 3px; }
|
|
.stop-card.dragging { opacity: 0.5; }
|
|
.truck-column { min-height: 200px; }
|
|
#pickup-calendar { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; font-size: 12px; }
|
|
.cal-day { text-align: center; padding: 6px 4px; border-radius: 6px; }
|
|
.cal-day.has-pickups { background: #D6E4F0; cursor: pointer; }
|
|
.cal-day.today { background: #2F5496; color: #fff; }
|
|
.cal-day .day-num { font-weight: 600; }
|
|
.cal-day .day-count { font-size: 10px; font-weight: 700; }
|
|
</style>
|
|
`);
|
|
|
|
|
|
// ── 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 || []);
|
|
// weekly chart removed
|
|
$("#pickup-count-label").text((r.message.pickups || []).length + " pickups");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function renderPickupTable(pickups) {
|
|
var tbody = $("#pickup-tbody");
|
|
if (!pickups.length) {
|
|
tbody.html('<tr><td colspan="14" class="text-center text-muted">No scheduled pickups</td></tr>');
|
|
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"
|
|
? '<span class="badge" style="background:#E3F2FD;color:#1565C0">Drop-off</span>'
|
|
: '<span class="badge" style="background:#FFF3E0;color:#E65100">Pickup</span>';
|
|
h += '<tr style="cursor:pointer" onclick=\"window.open(\'/app/scheduled-pickup/\' + encodeURIComponent(p.name) + \'\', \'_blank\')\">';
|
|
h += '<td>' + esc(p.pickup_date || "") + '</td>';
|
|
h += '<td class="text-muted">' + weekday + '</td>';
|
|
h += '<td>' + typeBadge + '</td>';
|
|
h += '<td><strong>' + esc(p.company_name || p.customer_number || "") + '</strong></td>';
|
|
h += '<td style="font-size:12px">' + esc((p.contact_name || "") + (p.contact_phone ? " • " + p.contact_phone : "")) + '</td>';
|
|
h += '<td style="font-size:12px">' + esc((p.address_line || "") + (p.city ? ", " + p.city : "")) + '</td>';
|
|
h += '<td class="text-right">' + (p.estimated_items || "—") + '</td>';
|
|
h += '<td>' + esc(p.data_status || "—") + '</td>';
|
|
h += '<td>' + esc(p.red_r2 || "—") + '</td>';
|
|
h += '<td><span class="badge" style="background:' + sc + '22;color:' + sc + '">' + esc(st) + '</span></td>';
|
|
h += '<td style="max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc(p.notes || "") + '</td>';
|
|
h += '<td>' + esc(p.truck || "—") + '</td>';
|
|
h += '<td class="text-center">' + (p.needs_aor ? "✓" : "") + '</td>';
|
|
h += '<td class="text-center">' + (p.needs_cod ? "✓" : "") + '</td>';
|
|
h += '</tr>';
|
|
});
|
|
tbody.html(h);
|
|
}
|
|
|
|
function renderCalendar(days) {
|
|
var el = $("#pickup-calendar");
|
|
if (!days || !days.length) { el.html('<div class="text-muted text-center">No upcoming pickups</div>'); return; }
|
|
var today = frappe.datetime.nowdate();
|
|
var h = '<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:4px;font-size:12px">';
|
|
["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].forEach(function(d) {
|
|
h += '<div style="text-align:center;font-weight:600;color:#666;padding:4px">' + d + '</div>';
|
|
});
|
|
var first = new Date(days[0].date + "T12:00:00");
|
|
for (var i = 0; i < first.getDay(); i++) h += '<div></div>';
|
|
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 += '<div class="' + bg + '" ' + onclick + '>';
|
|
h += '<div style="font-weight:' + (isToday ? "700" : "400") + '">' + d.date.split("-")[2] + '</div>';
|
|
if (hasCount) h += '<div class="day-count">' + d.count + '</div>';
|
|
h += '</div>';
|
|
});
|
|
h += '</div>';
|
|
el.html(h);
|
|
}
|
|
|
|
// ── 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 = '<div class="stop-card" data-pickup="' + esc(p.name) + '">';
|
|
if (order) h += '<div style="font-size:11px;color:#666;margin-bottom:2px">Stop #' + order + '</div>';
|
|
h += '<div class="stop-co">' + esc(p.company_name || p.customer_number || "Unknown") + '</div>';
|
|
h += '<div class="stop-addr">' + esc((p.address_line || "") + (p.city ? ", " + p.city : "")) + '</div>';
|
|
h += '<div class="stop-meta">';
|
|
if (p.estimated_items) h += '<span>' + p.estimated_items + ' items</span>';
|
|
if (p.data_status) h += '<span>' + esc(p.data_status) + '</span>';
|
|
if (p.red_r2) h += '<span>' + esc(p.red_r2) + '</span>';
|
|
if (p.needs_aor) h += '<span>AoR</span>';
|
|
if (p.needs_cod) h += '<span>CoD</span>';
|
|
h += '</div></div>';
|
|
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('<tr><td colspan="9" class="text-center text-muted">No check-ins yet</td></tr>');
|
|
return;
|
|
}
|
|
tbody.html(checkins.map(function(c) {
|
|
return '<tr><td>' + esc(c.pickup_date || "") + '</td>' +
|
|
'<td><strong>' + esc(c.company_name || "") + '</strong></td>' +
|
|
'<td>' + esc(c.pickup_type || "") + '</td>' +
|
|
'<td class="text-right">' + (c.estimated_items || "—") + '</td>' +
|
|
'<td class="text-right">' + (c.estimated_weight || "—") + '</td>' +
|
|
'<td style="max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc(c.load_contents || "") + '</td>' +
|
|
'<td>' + esc(c.data_status || "—") + '</td>' +
|
|
'<td>' + esc(c.red_r2 || "—") + '</td>' +
|
|
'<td>' + esc(c.status || "") + '</td></tr>';
|
|
}).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, ">").replace(/"/g, """) : ""; }
|
|
function dayName(d) { return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][d.getDay()]; }
|
|
|
|
// ── Init ──
|
|
loadPickups();
|
|
};
|