frappe.pages['intake'].on_page_load = function(wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, title: 'Intake Station', single_column: true }); page.set_primary_action('New Intake', function() { show_intake_form(); }, 'add'); page.add_inner_button('Refresh', function() { load_recent_pallets(); }); $(wrapper).find('.layout-main-section').html(`
Recent Pallets
Status Customer # Driver Received RED/R2 Items Weight Actions
Loading...
`); // Build ERPNext Link controls for Customer Number and Supplier setup_link_controls(); set_today_date(); load_recent_pallets(); $('#received_date').on('change', function() { var d = new Date($(this).val() + 'T12:00:00'); var days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; $('#weekday').val(days[d.getDay()] || ''); }); $('#intake-form').on('submit', function(e) { e.preventDefault(); save_pallet(); }); $('#btn-cancel').on('click', function() { $('#intake-form-container').hide(); $('#recent-pallets').show(); clear_form(); }); $('#btn-print-labels').on('click', function() { print_labels(); }); }; // ── ERPNext Link Controls ────────────────────────────────────────────── var customer_number_control = null; var supplier_control = null; function setup_link_controls() { // Customer Number — Link to Supplier, searching by name (which IS the customer number) customer_number_control = frappe.ui.form.make_control({ parent: $('#customer-number-control'), df: { fieldtype: 'Link', fieldname: 'customer_number', options: 'Customer', label: 'Customer Number', reqd: 1, placeholder: 'Search customer number...', onchange: function() { var val = customer_number_control.get_value(); if (val) { fetch_customer_details(val); } else { clear_customer_fields(); } } }, only_input: true, }); customer_number_control.refresh(); // Style to match form $('#customer-number-control .control-input').css('margin', '0'); $('#customer-number-control .help-box').remove(); // Supplier (Driver) — Link to Supplier for driver name supplier_control = frappe.ui.form.make_control({ parent: $('#supplier-control'), df: { fieldtype: 'Link', fieldname: 'supplier', options: 'Customer', label: 'Driver', placeholder: 'Search driver...', onchange: function() { var val = supplier_control.get_value(); if (val && val !== customer_number_control.get_value()) { // Driver selected separately — don't override customer_number } } }, only_input: true, }); supplier_control.refresh(); $('#supplier-control .control-input').css('margin', '0'); $('#supplier-control .help-box').remove(); } function fetch_customer_details(customer_name) { frappe.call({ method: 'frappe.client.get', args: {doctype: 'Customer', name: customer_name}, callback: function(r) { if (r.message) { var c = r.message; // Auto-fill company name if (c.customer_name && !$('#company_name').val()) { $('#company_name').val(c.customer_name); } // Auto-fill custom CRM fields if (c.contact_persons && !$('#contact_persons').val()) { $('#contact_persons').val(c.contact_persons); } if (c.hours_of_operation && !$('#hours_of_operation').val()) { $('#hours_of_operation').val(c.hours_of_operation); } if (c.legacy_notes && !$('#legacy_notes').val()) { $('#legacy_notes').val(c.legacy_notes); } // Driver is independent — don't auto-populate from customer number // Fill contact fields from Supplier doc's native fields first // ERPNext auto-populates these from the primary Contact/Address if (c.customer_primary_contact) { // Fetch the Contact doc for name/phone/email frappe.call({ method: 'frappe.client.get', args: {doctype: 'Contact', name: c.customer_primary_contact}, callback: function(cr) { if (cr.message) { var ct = cr.message; var full_name = [ct.first_name, ct.last_name].filter(Boolean).join(' '); if (full_name && !$('#contact_name').val()) { $('#contact_name').val(full_name); } if (ct.email_id && !$('#contact_email').val()) { $('#contact_email').val(ct.email_id); } if (ct.phone && !$('#contact_number').val()) { $('#contact_number').val(ct.phone); } } } }); } else { // Fallback: use Supplier-level fields (mobile_no, email_id) if (c.mobile_no && !$('#contact_number').val()) { $('#contact_number').val(c.mobile_no); } if (c.email_id && !$('#contact_email').val()) { $('#contact_email').val(c.email_id); } // Last resort: search for any linked Contact frappe.call({ method: 'frappe.client.get_list', args: { doctype: 'Contact', filters: [['Dynamic Link', 'link_name', '=', customer_name]], fields: ['name', 'first_name', 'last_name', 'email_id', 'phone'], limit_page_length: 1 }, callback: function(cr) { if (cr.message && cr.message.length > 0) { var c = cr.message[0]; var full_name = [c.first_name, c.last_name].filter(Boolean).join(' '); if (full_name && !$('#contact_name').val()) { $('#contact_name').val(full_name); } if (c.email_id && !$('#contact_email').val()) { $('#contact_email').val(c.email_id); } if (c.phone && !$('#contact_number').val()) { $('#contact_number').val(c.phone); } } } }); } // Fill address from Supplier doc's primary_address or linked Address if (c.primary_address && !$('#address_line').val()) { $('#address_line').val(c.primary_address); } else if (c.customer_primary_address) { frappe.call({ method: 'frappe.client.get', args: {doctype: 'Address', name: c.customer_primary_address}, callback: function(ar) { if (ar.message) { var a = ar.message; var addr = [a.address_line1, a.address_line2, a.city, a.state].filter(Boolean).join(', '); if (addr && !$('#address_line').val()) { $('#address_line').val(addr); } } } }); } else { // Fallback: search for any linked Address frappe.call({ method: 'frappe.client.get_list', args: { doctype: 'Address', filters: [['Dynamic Link', 'link_name', '=', customer_name]], fields: ['name', 'address_line1', 'city', 'state'], limit_page_length: 1 }, callback: function(ar) { if (ar.message && ar.message.length > 0) { var a = ar.message[0]; var addr = [a.address_line1, a.city, a.state].filter(Boolean).join(', '); if (addr && !$('#address_line').val()) { $('#address_line').val(addr); } } } }); } } } }); } function clear_customer_fields() { $('#company_name').val(''); $('#contact_name').val(''); $('#contact_number').val(''); $('#contact_email').val(''); $('#address_line').val(''); if (supplier_control) supplier_control.set_value(''); } // ── Form Helpers ────────────────────────────────────────────────────── function set_today_date() { var today = new Date().toISOString().split('T')[0]; $('#received_date').val(today); $('#received_date').trigger('change'); } function show_intake_form() { $('#intake-form-container').show(); $('#recent-pallets').hide(); set_today_date(); // Focus the customer number control setTimeout(function() { if (customer_number_control) { customer_number_control.$input.focus(); } }, 200); } function clear_form() { $('#intake-form input[type="text"], #intake-form input[type="tel"], #intake-form input[type="email"], #intake-form input[type="number"], #intake-form input[type="date"], #intake-form textarea').val(''); $('#legacy_notes').val(''); $('#intake-form select').val(''); $('#total_items').val('0'); $('#num_labels').val('1'); $('#amount').val('0'); $('#needs_aor').prop('checked', false); $('#needs_cod').prop('checked', false); $('#btn-print-labels').prop('disabled', true); $('#save-status').text(''); $('#intake-form-container').data('pallet-name', ''); if (customer_number_control) customer_number_control.set_value(''); if (supplier_control) supplier_control.set_value(''); } function save_pallet() { var pallet_name = $('#intake-form-container').data('pallet-name'); var is_new = !pallet_name; var data = { doctype: 'Pallet', received_date: $('#received_date').val(), customer_number: customer_number_control ? customer_number_control.get_value() : '', customer: customer_number_control ? customer_number_control.get_value() : '', supplier: supplier_control ? supplier_control.get_value() : '', company_name: $('#company_name').val(), pickup: $('#pickup').val(), data_status: $('#data_status').val(), red_r2: $('#red_r2').val(), barcode: $('#barcode').val(), total_items: parseInt($('#total_items').val()) || 0, num_labels: parseInt($('#num_labels').val()) || 1, contact_name: $('#contact_name').val(), contact_persons: $('#contact_persons').val(), contact_number: $('#contact_number').val(), contact_email: $('#contact_email').val(), address_line: $('#address_line').val(), hours_of_operation: $('#hours_of_operation').val(), legacy_notes: $('#legacy_notes').val(), weights: $('#weights').val(), invoice_check_request: $('#invoice_check_request').val(), amount: parseFloat($('#amount').val()) || 0, paid_received: $('#paid_received').val(), needs_aor: $('#needs_aor').is(':checked') ? 1 : 0, needs_cod: $('#needs_cod').is(':checked') ? 1 : 0, notes: $('#notes').val(), status: 'Received' }; if (!is_new) { data.name = pallet_name; } frappe.call({ method: is_new ? 'frappe.client.insert' : 'frappe.client.update', args: { doc: data }, callback: function(r) { if (r.message) { var name = r.message.name; $('#save-status').html(' Saved as Intake ' + name + ''); $('#btn-print-labels').prop('disabled', false); if (is_new) { $('#intake-form-container').data('pallet-name', name); } } }, error: function(r) { var msg = 'Unknown error'; if (r && r._server_messages) { try { var msgs = JSON.parse(r._server_messages); if (msgs && msgs.length) { var errObj = JSON.parse(msgs[0]); msg = errObj.message || msg; } } catch(e) {} } $('#save-status').html(' Error: ' + msg + ''); } }); } function load_recent_pallets() { frappe.call({ method: 'frappe.client.get_list', args: { doctype: 'Pallet', fields: ['name', 'pallet_number', 'status', 'customer_number', 'company_name', 'supplier', 'received_date', 'red_r2', 'total_items', 'weights', 'notes', 'needs_aor', 'needs_cod'], limit_page_length: 20, order_by: 'creation desc' }, callback: function(r) { var tbody = $('#pallet-tbody'); tbody.empty(); if (!r.message || r.message.length === 0) { tbody.append('No pallets yet'); return; } r.message.forEach(function(p) { var status_class = { 'Received': 'info', 'Sorting': 'warning', 'Processing': 'primary', 'Complete': 'success', 'Shipped': 'default' }[p.status] || 'default'; tbody.append( '' + '' + (p.status || 'Received') + '' + '' + (p.customer_number || '') + '' + '' + (p.supplier || '') + '' + '' + (p.received_date || '') + '' + '' + (p.red_r2 || '') + (p.needs_aor ? ' AoR' : '') + (p.needs_cod ? ' COD' : '') + '' + '' + (p.total_items || 0) + '' + '' + (p.weights || '') + '' + '' + ' ' + '' + '' + '' ); }); } }); } function edit_pallet(name) { frappe.call({ method: 'frappe.client.get', args: {doctype: 'Pallet', name: name}, callback: function(r) { if (r.message) { var d = r.message; $('#intake-form-container').show(); $('#recent-pallets').hide(); $('#intake-form-container').data('pallet-name', name); $('#received_date').val(d.received_date || ''); if (customer_number_control) customer_number_control.set_value(d.customer_number || ''); if (supplier_control) supplier_control.set_value(d.supplier || ''); $('#company_name').val(d.company_name || ''); $('#pickup').val(d.pickup || ''); $('#data_status').val(d.data_status || ''); $('#red_r2').val(d.red_r2 || ''); $('#barcode').val(d.barcode || ''); $('#total_items').val(d.total_items || 0); $('#num_labels').val(d.num_labels || 1); $('#contact_name').val(d.contact_name || ''); $('#contact_number').val(d.contact_number || ''); $('#contact_email').val(d.contact_email || ''); $('#address_line').val(d.address_line || ''); $('#weights').val(d.weights || ''); $('#invoice_check_request').val(d.invoice_check_request || ''); $('#amount').val(d.amount || 0); $('#paid_received').val(d.paid_received || ''); $('#needs_aor').prop('checked', d.needs_aor === 1); $('#needs_cod').prop('checked', d.needs_cod === 1); $('#notes').val(d.notes || ''); $('#received_date').trigger('change'); $('#btn-print-labels').prop('disabled', false); } } }); } function print_labels() { var pallet_name = $('#intake-form-container').data('pallet-name'); if (!pallet_name) return; print_pallet_labels(pallet_name); } function print_pallet_labels(name) { frappe.call({ method: 'frappe.client.get', args: {doctype: 'Pallet', name: name}, callback: function(r) { if (r.message) { generate_zpl_label(r.message); } } }); } function generate_zpl_label(d) { var label_count = d.num_labels || 1; var date_str = d.received_date || ''; var driver = d.supplier || ''; var customer_num = d.customer_number || ''; var red_r2 = d.red_r2 || ''; var weight = d.weights || ''; var pallet_num = d.pallet_number || d.name || ''; var qr_url = window.location.origin + '/app/pallet/' + (d.name || pallet_num); var logo_url = window.location.origin + '/files/COR_html_952bb51d.png'; var labels_html = ''; for (var i = 1; i <= label_count; i++) { labels_html += '
'; labels_html += '
WESTECH RECYCLERS
'; labels_html += '
'; labels_html += '
'; labels_html += '
'; labels_html += '
Pallet #: ' + pallet_num + '
'; if (customer_num) { labels_html += '
Customer #: ' + customer_num + '
'; } labels_html += '
Received: ' + date_str + '
'; labels_html += '
Driver: ' + driver + '
'; labels_html += '
Weight: ' + weight + '
'; labels_html += '
Items: ' + (d.total_items || 0) + '
'; labels_html += '
'; labels_html += '
'; labels_html += '
'; if (red_r2) { labels_html += '
' + red_r2 + '
'; } labels_html += ''; labels_html += '
'; } var existing = document.getElementById('label-preview-overlay'); if (existing) existing.remove(); var overlay = document.createElement('div'); overlay.id = 'label-preview-overlay'; overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.6);z-index:9999;display:flex;align-items:center;justify-content:center;'; var modal = document.createElement('div'); modal.style.cssText = 'background:#fff;border-radius:8px;padding:20px;max-height:90vh;overflow-y:auto;box-shadow:0 4px 20px rgba(0,0,0,0.3);max-width:95vw;'; var title = document.createElement('h3'); title.textContent = 'Label Preview — Pallet ' + pallet_num + ' (' + label_count + ' label' + (label_count > 1 ? 's' : '') + ')'; title.style.cssText = 'margin:0 0 15px 0;font-size:16pt;'; var btnGroup = document.createElement('div'); btnGroup.style.cssText = 'margin-bottom:15px;display:flex;gap:10px;'; var printBtn = document.createElement('button'); printBtn.className = 'btn btn-primary'; printBtn.innerHTML = ' Print Labels'; printBtn.onclick = function() { var pw = window.open('', '_blank'); pw.document.write('Labels — Pallet ' + pallet_num + ''); pw.document.write('