From ee57b903d6a9620fd16a7d9823bf19cc3253a9b8 Mon Sep 17 00:00:00 2001 From: Westech Admin Date: Thu, 21 May 2026 05:40:30 +0000 Subject: [PATCH] feat(receiving): add Receiving page with 3-stage workflow --- .../__pycache__/__init__.cpython-312.pyc | Bin 188 -> 0 bytes westech_r2/__pycache__/hooks.cpython-312.pyc | Bin 862 -> 0 bytes westech_r2/api/__init__.py | 1 + .../api/__pycache__/__init__.cpython-312.pyc | Bin 213 -> 0 bytes .../optimize_routes.cpython-312.pyc | Bin 5337 -> 0 bytes .../api/__pycache__/sales.cpython-312.pyc | Bin 3856 -> 0 bytes westech_r2/api/receiving_api.py | 322 ++++++++ .../__pycache__/__init__.cpython-312.pyc | Bin 179 -> 0 bytes .../__pycache__/pallet_list.cpython-312.pyc | Bin 4720 -> 0 bytes .../receiving}/__init__.py | 0 westech_r2/page/receiving/receiving.js | 598 ++++++++++++++ .../receiving/receiving.json} | 11 +- .../intake.py => page/receiving/receiving.py} | 2 +- .../__pycache__/__init__.cpython-312.pyc | Bin 181 -> 0 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 173 -> 0 bytes westech_r2/westech_r2/doctype/__init__.py | 0 .../doctype/customer_record/__init__.py | 0 .../customer_record/customer_record.js | 8 - .../customer_record/customer_record.json | 158 ---- .../customer_record/customer_record.py | 9 - .../customer_record/test_customer_record.py | 9 - westech_r2/westech_r2/page/__init__.py | 0 .../page/customer_intake/__init__.py | 0 .../page/customer_intake/customer-intake.json | 1 - .../page/customer_intake/customer_intake.html | 96 --- .../page/customer_intake/customer_intake.js | 125 --- .../page/customer_intake/customer_intake.py | 84 -- .../page/customer_records/__init__.py | 0 .../customer_records/customer-records.json | 1 - .../customer_records/customer_records.html | 227 ----- .../page/customer_records/customer_records.js | 7 - .../customer_records/customer_records.json | 23 - .../page/customer_records/customer_records.py | 85 -- .../westech_r2/page/ebay-pricing/__init__.py | 0 .../page/ebay-pricing/ebay-pricing.css | 5 - .../page/ebay-pricing/ebay-pricing.html | 8 - .../page/ebay-pricing/ebay-pricing.js | 323 -------- .../page/ebay-pricing/ebay-pricing.json | 26 - .../page/ebay-pricing/ebay-pricing.py | 1 - .../page/ebay-pricing/ebay_pricing.css | 5 - .../page/ebay-pricing/ebay_pricing.html | 8 - .../page/ebay-pricing/ebay_pricing.js | 1 - .../page/ebay-pricing/ebay_pricing.json | 1 - .../page/ebay-pricing/ebay_pricing.py | 1 - .../westech_r2/page/eim_portal/__init__.py | 0 .../westech_r2/page/eim_portal/eim-portal.css | 1 - .../westech_r2/page/eim_portal/eim-portal.js | 4 - .../page/eim_portal/eim-portal.json | 13 - .../westech_r2/page/eim_portal/eim-portal.py | 5 - .../westech_r2/page/eim_portal/eim_portal.js | 7 - .../page/eim_portal/eim_portal.json | 23 - westech_r2/westech_r2/page/intake/__init__.py | 0 westech_r2/westech_r2/page/intake/intake.css | 11 - westech_r2/westech_r2/page/intake/intake.js | 772 ------------------ westech_r2/westech_r2/page/intake/intake.json | 23 - .../westech_r2/page/load_detail/__init__.py | 0 .../page/load_detail/load-detail.css | 1 - .../page/load_detail/load-detail.html | 53 -- .../page/load_detail/load-detail.js | 65 -- .../page/load_detail/load-detail.json | 18 - .../page/load_detail/load_detail.css | 1 - .../page/load_detail/load_detail.html | 53 -- .../page/load_detail/load_detail.js | 65 -- .../page/load_detail/load_detail.json | 18 - .../westech_r2/page/load_update/__init__.py | 0 .../page/load_update/load-update.css | 1 - .../page/load_update/load-update.html | 4 - .../page/load_update/load-update.js | 109 --- .../page/load_update/load-update.json | 18 - .../page/load_update/load-update.py | 17 - .../page/load_update/load_update.css | 1 - .../page/load_update/load_update.html | 4 - .../page/load_update/load_update.js | 109 --- .../page/load_update/load_update.json | 18 - .../page/load_update/load_update.py | 17 - .../westech_r2/page/pallet_list/__init__.py | 1 - .../page/pallet_list/pallet-list.html | 82 -- .../page/pallet_list/pallet-list.js | 128 --- .../page/pallet_list/pallet-list.json | 19 - .../page/pallet_list/pallet-list.py | 64 -- .../page/pallet_list/pallet_list.html | 82 -- .../page/pallet_list/pallet_list.js | 128 --- .../page/pallet_list/pallet_list.json | 19 - .../page/pallet_list/pallet_list.py | 64 -- .../westech_r2/page/r2_tracking/__init__.py | 0 .../page/r2_tracking/r2-tracking.css | 1 - .../page/r2_tracking/r2-tracking.js | 4 - .../page/r2_tracking/r2-tracking.json | 13 - .../page/r2_tracking/r2-tracking.py | 5 - .../page/r2_tracking/r2_tracking.js | 7 - .../page/route_planner/route-planner.html | 4 - .../page/route_planner/route-planner.js | 38 - .../page/route_planner/route-planner.json | 14 - .../page/route_planner/route_planner.html | 4 - .../page/route_planner/route_planner.js | 38 - .../page/route_planner/route_planner.json | 9 - westech_r2/westech_r2/page/wes-ai/__init__.py | 0 westech_r2/westech_r2/page/wes-ai/wes-ai.css | 1 - westech_r2/westech_r2/page/wes-ai/wes-ai.js | 4 - westech_r2/westech_r2/page/wes-ai/wes-ai.json | 13 - westech_r2/westech_r2/page/wes-ai/wes-ai.py | 5 - westech_r2/westech_r2/page/wes-ai/wes_ai.js | 30 - .../westech_r2/workspace/westech/westech.json | 363 -------- 103 files changed, 928 insertions(+), 3789 deletions(-) delete mode 100644 westech_r2/__pycache__/__init__.cpython-312.pyc delete mode 100644 westech_r2/__pycache__/hooks.cpython-312.pyc delete mode 100644 westech_r2/api/__pycache__/__init__.cpython-312.pyc delete mode 100644 westech_r2/api/__pycache__/optimize_routes.cpython-312.pyc delete mode 100644 westech_r2/api/__pycache__/sales.cpython-312.pyc create mode 100644 westech_r2/api/receiving_api.py delete mode 100644 westech_r2/page/pallet_list/__pycache__/__init__.cpython-312.pyc delete mode 100644 westech_r2/page/pallet_list/__pycache__/pallet_list.cpython-312.pyc rename westech_r2/{westech_r2 => page/receiving}/__init__.py (100%) create mode 100644 westech_r2/page/receiving/receiving.js rename westech_r2/{westech_r2/page/r2_tracking/r2_tracking.json => page/receiving/receiving.json} (60%) rename westech_r2/{westech_r2/page/intake/intake.py => page/receiving/receiving.py} (66%) delete mode 100644 westech_r2/page/route_planner/__pycache__/__init__.cpython-312.pyc delete mode 100644 westech_r2/westech_r2/__pycache__/__init__.cpython-312.pyc delete mode 100644 westech_r2/westech_r2/doctype/__init__.py delete mode 100644 westech_r2/westech_r2/doctype/customer_record/__init__.py delete mode 100644 westech_r2/westech_r2/doctype/customer_record/customer_record.js delete mode 100644 westech_r2/westech_r2/doctype/customer_record/customer_record.json delete mode 100644 westech_r2/westech_r2/doctype/customer_record/customer_record.py delete mode 100644 westech_r2/westech_r2/doctype/customer_record/test_customer_record.py delete mode 100644 westech_r2/westech_r2/page/__init__.py delete mode 100644 westech_r2/westech_r2/page/customer_intake/__init__.py delete mode 100644 westech_r2/westech_r2/page/customer_intake/customer-intake.json delete mode 100644 westech_r2/westech_r2/page/customer_intake/customer_intake.html delete mode 100644 westech_r2/westech_r2/page/customer_intake/customer_intake.js delete mode 100644 westech_r2/westech_r2/page/customer_intake/customer_intake.py delete mode 100644 westech_r2/westech_r2/page/customer_records/__init__.py delete mode 100644 westech_r2/westech_r2/page/customer_records/customer-records.json delete mode 100644 westech_r2/westech_r2/page/customer_records/customer_records.html delete mode 100644 westech_r2/westech_r2/page/customer_records/customer_records.js delete mode 100644 westech_r2/westech_r2/page/customer_records/customer_records.json delete mode 100644 westech_r2/westech_r2/page/customer_records/customer_records.py delete mode 100644 westech_r2/westech_r2/page/ebay-pricing/__init__.py delete mode 100644 westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.css delete mode 100644 westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.html delete mode 100644 westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.js delete mode 100644 westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.json delete mode 100644 westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.py delete mode 100644 westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.css delete mode 100644 westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.html delete mode 120000 westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.js delete mode 120000 westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.json delete mode 100644 westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.py delete mode 100644 westech_r2/westech_r2/page/eim_portal/__init__.py delete mode 100644 westech_r2/westech_r2/page/eim_portal/eim-portal.css delete mode 100644 westech_r2/westech_r2/page/eim_portal/eim-portal.js delete mode 100644 westech_r2/westech_r2/page/eim_portal/eim-portal.json delete mode 100644 westech_r2/westech_r2/page/eim_portal/eim-portal.py delete mode 100644 westech_r2/westech_r2/page/eim_portal/eim_portal.js delete mode 100644 westech_r2/westech_r2/page/eim_portal/eim_portal.json delete mode 100644 westech_r2/westech_r2/page/intake/__init__.py delete mode 100644 westech_r2/westech_r2/page/intake/intake.css delete mode 100644 westech_r2/westech_r2/page/intake/intake.js delete mode 100644 westech_r2/westech_r2/page/intake/intake.json delete mode 100644 westech_r2/westech_r2/page/load_detail/__init__.py delete mode 100644 westech_r2/westech_r2/page/load_detail/load-detail.css delete mode 100644 westech_r2/westech_r2/page/load_detail/load-detail.html delete mode 100644 westech_r2/westech_r2/page/load_detail/load-detail.js delete mode 100644 westech_r2/westech_r2/page/load_detail/load-detail.json delete mode 100644 westech_r2/westech_r2/page/load_detail/load_detail.css delete mode 100644 westech_r2/westech_r2/page/load_detail/load_detail.html delete mode 100644 westech_r2/westech_r2/page/load_detail/load_detail.js delete mode 100644 westech_r2/westech_r2/page/load_detail/load_detail.json delete mode 100644 westech_r2/westech_r2/page/load_update/__init__.py delete mode 100644 westech_r2/westech_r2/page/load_update/load-update.css delete mode 100644 westech_r2/westech_r2/page/load_update/load-update.html delete mode 100644 westech_r2/westech_r2/page/load_update/load-update.js delete mode 100644 westech_r2/westech_r2/page/load_update/load-update.json delete mode 100644 westech_r2/westech_r2/page/load_update/load-update.py delete mode 100644 westech_r2/westech_r2/page/load_update/load_update.css delete mode 100644 westech_r2/westech_r2/page/load_update/load_update.html delete mode 100644 westech_r2/westech_r2/page/load_update/load_update.js delete mode 100644 westech_r2/westech_r2/page/load_update/load_update.json delete mode 100644 westech_r2/westech_r2/page/load_update/load_update.py delete mode 100644 westech_r2/westech_r2/page/pallet_list/__init__.py delete mode 100644 westech_r2/westech_r2/page/pallet_list/pallet-list.html delete mode 100644 westech_r2/westech_r2/page/pallet_list/pallet-list.js delete mode 100644 westech_r2/westech_r2/page/pallet_list/pallet-list.json delete mode 100644 westech_r2/westech_r2/page/pallet_list/pallet-list.py delete mode 100644 westech_r2/westech_r2/page/pallet_list/pallet_list.html delete mode 100644 westech_r2/westech_r2/page/pallet_list/pallet_list.js delete mode 100644 westech_r2/westech_r2/page/pallet_list/pallet_list.json delete mode 100644 westech_r2/westech_r2/page/pallet_list/pallet_list.py delete mode 100644 westech_r2/westech_r2/page/r2_tracking/__init__.py delete mode 100644 westech_r2/westech_r2/page/r2_tracking/r2-tracking.css delete mode 100644 westech_r2/westech_r2/page/r2_tracking/r2-tracking.js delete mode 100644 westech_r2/westech_r2/page/r2_tracking/r2-tracking.json delete mode 100644 westech_r2/westech_r2/page/r2_tracking/r2-tracking.py delete mode 100644 westech_r2/westech_r2/page/r2_tracking/r2_tracking.js delete mode 100644 westech_r2/westech_r2/page/route_planner/route-planner.html delete mode 100644 westech_r2/westech_r2/page/route_planner/route-planner.js delete mode 100644 westech_r2/westech_r2/page/route_planner/route-planner.json delete mode 100644 westech_r2/westech_r2/page/route_planner/route_planner.html delete mode 100644 westech_r2/westech_r2/page/route_planner/route_planner.js delete mode 100644 westech_r2/westech_r2/page/route_planner/route_planner.json delete mode 100644 westech_r2/westech_r2/page/wes-ai/__init__.py delete mode 100644 westech_r2/westech_r2/page/wes-ai/wes-ai.css delete mode 100644 westech_r2/westech_r2/page/wes-ai/wes-ai.js delete mode 100644 westech_r2/westech_r2/page/wes-ai/wes-ai.json delete mode 100644 westech_r2/westech_r2/page/wes-ai/wes-ai.py delete mode 100644 westech_r2/westech_r2/page/wes-ai/wes_ai.js delete mode 100644 westech_r2/westech_r2/workspace/westech/westech.json diff --git a/westech_r2/__pycache__/__init__.cpython-312.pyc b/westech_r2/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 0b2fbe3d184f24793d3a5fa0ea62ab65216276c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 188 zcmX@j%ge<81UtWSWQhUk#~=<2FhUuhIe?7m3@Hpz43&(UOjWD~dIoxiewvK8xZ~r? zQj3Z+^Yh~4S2BDCssH7upOK%Ns-ISrSWu9vpITIqms(Mxo0OWDoS_dCD%LMgEiOq- z&WJBELT1LtXXa&=#K-FuRQ}?y$<0qG%}KQ@Vgs58az-)8l^>WH85wWzi8gWiRM DuWAPb9ZhVPlmsM zjfIJgzlEh^bSwWrpj#)-PC_feNq+u5zaRI#_x!_k9Yde@pW5e7tA_EjAj?xQ`~iui?61KQnOSo9Q=j^UU;{*upk3wRN;@;dY5#Z989UzBTD? z^ptZ6LP8h?e3&`MbL<@qRda!}X9q*?h@)^s;xq4*aqn*sgp-g07s^o8ce_U$HPw8@ z!lOw7S$jJbl8wC`0u*QNHYE^C?=a*fk;*=1d?XSSf@%U!Vz`p3bppCJ1Oi<^HoVi+ z$1Ie(Vn0EYf?U4R1S6@VffGc7n2BubKRF_d0H+jikdSbcCjEaufD9y&6qI#=C@G3& z&+k=23F$8`i5MG+{xT|v5XYBDE0%g@BHO%|w3JL4!u`^Oh=wUG6iX%%6?#;S{j#B~ zceoVk?Uy1m`>^cF+heNsR+re@u$6_9B#6-%lwF{?WVe7xdP0eafU9mnF^F==gvIkv z7$ZVetME}0LM(Kvr{qee9E5T?d`Jn`1JZt>9IZTn3%x4w-MA@!xZABk#Ktf<d_kB1YRx-J@UXEF@f-SzT%bEHSu{N`+X`0j4T5e6-k8^w4ThHC; z%8R@^ZFe;7tmduXje5J1TeF_A^5kesSCf^c TXGasa@#~@6seE+!n!fT6k%uEM diff --git a/westech_r2/api/__init__.py b/westech_r2/api/__init__.py index 9219fdc..99e389c 100644 --- a/westech_r2/api/__init__.py +++ b/westech_r2/api/__init__.py @@ -1,2 +1,3 @@ from westech_r2.api import sales +from westech_r2.api import receiving_api diff --git a/westech_r2/api/__pycache__/__init__.cpython-312.pyc b/westech_r2/api/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 5dd511c12dc20ef002d5ef27e2e760fef46128f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 213 zcmX@j%ge<81dDaKvXp@IV-N=hn4pZ$0zk%eh7^Vr#vF!R#wbQchDs()=9eI8O~zZS z#fdqo#eSMhxA@9ai%U|IGvbSk^b!j)iqx(T|VM%*!l^kJl@x{Ka9Do1apelWJGQ0W_5nh>Jmt Q56p~=jL#Spir9c00EFx|^8f$< diff --git a/westech_r2/api/__pycache__/optimize_routes.cpython-312.pyc b/westech_r2/api/__pycache__/optimize_routes.cpython-312.pyc deleted file mode 100644 index 2be405b45b83e1ce6c6075f6c705642742652f26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5337 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 Zgwog6gkNWU4j!Od*H58sLh>7s^=", str(from_date)], + ["Scheduled Pickup", "pickup_date", "<=", str(to_date)]], + limit_page_length=500, + ) + + pickup_counts = {} + for p in all_pickups: + d = p.get("pickup_date", "") + if d: + pickup_counts[d] = pickup_counts.get(d, 0) + 1 + + calendar = [] + for i in range(30): + d = add_days(from_date, i) + ds = str(d) + calendar.append({"date": ds, "count": pickup_counts.get(ds, 0)}) + + # Build weekly chart data (last 12 weeks) + weekly = [] + for i in range(11, -1, -1): + week_start = add_days(from_date, -(from_date.weekday() + 7 * i)) + week_end = add_days(week_start, 6) + count = 0 + for d_str, c in pickup_counts.items(): + try: + d = getdate(d_str) + if week_start <= d <= week_end: + count += c + except (ValueError, TypeError): + pass + weekly.append({"label": week_start.strftime("%m/%d"), "count": count}) + + return { + "pickups": pickups, + "calendar": calendar, + "weekly": weekly, + } + + +@frappe.whitelist() +def auto_route(date=None): + """Auto-assign pickups to trucks based on capacity and proximity.""" + if not date: + date = today() + + pickups = frappe.get_list("Scheduled Pickup", + filters={"pickup_date": date}, + fields=["name", "company_name", "estimated_items", "estimated_weight", + "latitude", "longitude", "pickup_type"], + limit_page_length=200, + ) + + if not pickups: + return {"success": True, "assigned": 0} + + trucks = ["Truck 1", "Truck 2", "Truck 3"] + sorted_p = sorted(pickups, key=lambda p: (float(p.get("latitude") or 0), float(p.get("longitude") or 0))) + n = len(sorted_p) + assigned = 0 + + for i, p in enumerate(sorted_p): + if p.get("pickup_type") == "Drop-off": + truck = "" + else: + truck = trucks[i % 3] if n <= 3 else trucks[min(i * 3 // n, 2)] + + doc = frappe.get_doc("Scheduled Pickup", p["name"]) + doc.truck = truck + doc.status = "Routed" if truck else "Scheduled" + doc.stop_order = i + 1 + doc.save() + assigned += 1 + + frappe.db.commit() + return {"success": True, "assigned": assigned} + + +@frappe.whitelist() +def get_checkins(): + """Fetch completed check-ins.""" + checkins = frappe.get_list("Scheduled Pickup", + filters={"status": ["in", ["Complete", "In Progress"]]}, + fields=["name", "pickup_date", "pickup_type", "status", + "company_name", "customer_number", + "estimated_items", "estimated_weight", "load_contents", + "data_status", "red_r2", "notes"], + order_by="pickup_date desc", + limit_page_length=100, + ) + return {"checkins": checkins} + + +@frappe.whitelist() +def cor_report(): + """Generate Certificate of Recycling report.""" + pickups = frappe.get_list("Scheduled Pickup", + filters={"status": "Complete"}, + fields=["name", "pickup_date", "company_name", "customer_number", + "estimated_items", "estimated_weight", "load_contents", + "data_status", "red_r2", "needs_aor", "needs_cod"], + order_by="pickup_date desc", + limit_page_length=200, + ) + + html = "CoR Report" + html += "" + html += "

Certificate of Recycling (CoR) Report

" + html += "

Generated: " + frappe.utils.now() + "

" + html += "

Total completed loads: " + str(len(pickups)) + "

" + + if pickups: + html += "" + html += "" + for p in pickups: + html += "" + html += "" + html += "" + html += "" + html += "" + html += "" + html += "" + html += "" + html += "" + html += "
DateCustomerItemsWeightContentsData StatusRED/R2AoRCoD
" + str(p.get("pickup_date", "")) + "" + str(p.get("company_name", "")) + "" + str(p.get("estimated_items", "")) + "" + str(p.get("estimated_weight", "")) + "" + str(p.get("load_contents", "")) + "" + str(p.get("data_status", "")) + "" + str(p.get("red_r2", "")) + "" + ("✓" if p.get("needs_aor") else "") + "" + ("✓" if p.get("needs_cod") else "") + "
" + else: + html += "

No completed loads found.

" + + html += "" + frappe.local.response["type"] = "html" + frappe.local.response["page_content"] = html + + +@frappe.whitelist() +def print_route_sheet(date=None): + """Generate printable route sheet.""" + if not date: + date = today() + + filters = {"pickup_date": date} if date else {} + pickups = frappe.get_list("Scheduled Pickup", + filters=filters, + fields=["name", "pickup_date", "pickup_type", "status", "truck", "stop_order", + "company_name", "contact_name", "contact_phone", "contact_email", + "address_line", "city", "state", "zip_code", + "estimated_items", "estimated_weight", "load_contents", + "data_status", "red_r2", "needs_aor", "needs_cod", "notes"], + order_by="truck asc, stop_order asc", + limit_page_length=200, + ) + + trucks = {} + unassigned = [] + for p in pickups: + t = p.get("truck", "") + if t and t != "Unassigned": + trucks.setdefault(t, []).append(p) + else: + unassigned.append(p) + + html = "Route Sheet" + html += "" + html += "

Route Sheet — " + str(date or "Today") + "

" + + for truck_name, stops in sorted(trucks.items()): + html += '
🚛 ' + truck_name + " — " + str(len(stops)) + " stops
" + html += "" + html += "" + for i, s in enumerate(stops, 1): + addr = str(s.get("address_line", "")) + ", " + str(s.get("city", "")) + ", " + str(s.get("state", "")) + " " + str(s.get("zip_code", "")) + html += "" + html += "" + html += "" + html += "" + html += "" + html += "" + html += "" + html += "
#CustomerAddressContactItemsWeightDataRED/R2AoRCoDNotes
" + str(i) + "" + str(s.get("company_name", "")) + "" + addr + "" + str(s.get("contact_name", "")) + "
" + str(s.get("contact_phone", "")) + "
" + str(s.get("estimated_items", "")) + "" + str(s.get("estimated_weight", "")) + "" + str(s.get("data_status", "")) + "" + str(s.get("red_r2", "")) + "" + ("✓" if s.get("needs_aor") else "") + "" + ("✓" if s.get("needs_cod") else "") + "" + str(s.get("notes", "")) + "
" + + if unassigned: + html += "

Unassigned

" + for s in unassigned: + html += "" + html += "" + html += "" + html += "
CustomerAddressNotes
" + str(s.get("company_name", "")) + "" + str(s.get("address_line", "")) + "" + str(s.get("notes", "")) + "
" + + html += "" + frappe.local.response["type"] = "html" + frappe.local.response["page_content"] = html + + +@frappe.whitelist() +def print_green_sheet(date=None): + """Generate Green Sheet printout. + ⚠️ TODO: Green Sheet template needs to be filled in with the actual form layout.""" + if not date: + date = today() + + filters = {"pickup_date": date} if date else {} + pickups = frappe.get_list("Scheduled Pickup", + filters=filters, + fields=["name", "pickup_date", "pickup_type", "company_name", + "contact_name", "contact_phone", "address_line", "city", "state", "zip_code", + "estimated_items", "estimated_weight", "load_contents", + "data_status", "red_r2", "needs_aor", "needs_cod", "notes"], + order_by="truck asc, stop_order asc", + limit_page_length=200, + ) + + html = "Green Sheet" + html += "" + html += "

🟢 Green Sheet — " + str(date or "Today") + "

" + html += '

⚠️ This is a PLACEHOLDER template. Replace with actual Green Sheet layout once the form spec is provided.

' + + for p in pickups: + html += '
' + fields = [ + ("Customer", p.get("company_name", "")), + ("Contact", p.get("contact_name", "")), + ("Phone", p.get("contact_phone", "")), + ("Address", str(p.get("address_line", "")) + ", " + str(p.get("city", "")) + ", " + str(p.get("state", "")) + " " + str(p.get("zip_code", ""))), + ("Est. Items", p.get("estimated_items", "")), + ("Est. Weight", p.get("estimated_weight", "")), + ("Load Contents", p.get("load_contents", "")), + ("Data Status", p.get("data_status", "")), + ("RED/R2", p.get("red_r2", "")), + ("Needs AoR", "✓" if p.get("needs_aor") else ""), + ("Needs CoD", "✓" if p.get("needs_cod") else ""), + ("Notes", p.get("notes", "")), + ] + for label, val in fields: + html += '
' + label + ':
' + str(val) + '
' + html += '
' + + html += "" + frappe.local.response["type"] = "html" + frappe.local.response["page_content"] = html + + +@frappe.whitelist() +def print_labels(date=None): + """Generate printable labels.""" + if not date: + date = today() + + filters = {"pickup_date": date} if date else {} + pickups = frappe.get_list("Scheduled Pickup", + filters=filters, + fields=["name", "company_name", "pickup_date", "num_labels", "data_status", "red_r2"], + limit_page_length=200, + ) + + html = "Labels" + html += "" + + for p in pickups: + n = p.get("num_labels") or 1 + for _ in range(n): + html += '
' + html += '
' + str(p.get("company_name", "")) + '
' + html += '
' + str(p.get("pickup_date", "")) + '
' + html += '
' + str(p.get("data_status", "")) + " | " + str(p.get("red_r2", "")) + '
' + html += '
' + + html += "" + frappe.local.response["type"] = "html" + frappe.local.response["page_content"] = html diff --git a/westech_r2/page/pallet_list/__pycache__/__init__.cpython-312.pyc b/westech_r2/page/pallet_list/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 71fd083e0824af0c55beea74458a41286fc19322..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179 zcmX@j%ge<81SQFQSxP|qF^Gc>KC=KtrZZGBXfpb(WGG?+@;-yq{0h>~$j?pHPb*3+ zC`i>$Eh@-Mttin=O3h2o&<6??>zAh%m!u|V#1|PMGYb;aQ}qiHb8=Ek;&U>KOZ4O8 qGxIV_;^XxSDt~d<!4N1ma>4<0CU8BV!RWkOctXKrWvE diff --git a/westech_r2/page/pallet_list/__pycache__/pallet_list.cpython-312.pyc b/westech_r2/page/pallet_list/__pycache__/pallet_list.cpython-312.pyc deleted file mode 100644 index 5ff33f89c038b694045075f69a7728e3dca7cf98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4720 zcmdrPOKcn0asQRCW-d~+g8wNv(x#EAbW7vNXVh*x2*;|Ln8pdNhp1|J22u#4@gekQD zeNdY6G*8^aP3%45P%=A2>s34Ws3JrpK^CN20$(M6G?tZ>bW)J&DQm4|8tOQ!j_G)>PW8f2 z$xzRLPKGc0bt;ujBy^@9jo#At4VCixSg(4d+AL&lFvNCyRl>`u$G^=S4UPm@FDUBr z*0Lu8R|7Y$vEG~S_nz+mz^mG1C8}g)7Iyj)_o}Vc%8H7*R00&y>_EuRs$G+T(ZH23 zdu4no6z)F7v4a!iW2_RLzGirpeRn7@5nv@0HtOF?vZ-iN@Uc~@eQcFieXJzJ1QGlj zL3AHm<2xXvlbLAhcBH6FOiib=DLyhUh_iEwk5vR&5hPG5$T3OGC}KM0V?R=EN0f9# zjwS@z#}-8^2s2_zl;=w8rP83n$CkWNcTegmc6`Dgm|!pen7uME8Sbvy5IZo*PK73e zS3?0m$Ep@~G&mLvv#OOH9~_(vgxN5M>z1e-;l-E|?SokSzj991X&N~K`D8>)cg<0j znd1|+r;MV|%NhTw_U(SeYX|Zxg1(N1nz;Y&C+QDNUMZdBS=|y5w2+F;^}zPzo_UmxvAKwJu5yZe(^EQFWJ%@xOy;(3 zpM|Pia;wx0>!R*Pc0(!2VNdO@Mdj!HIsZ3J?v>y#T)E(mt7YZ-(%d?icfOh%d_gfS zOm6r^6Xv*z=lnZOZEN;b`?|l-bOJ;v*EZFfr&>4LdLD84whLeK`L@ej)RhXx8(Y+y zHR!)Bo*iq;wzVT~?O69cZ2N7;{f=#}KhO1ldAh(2At=H~Q4kvK(9rE{`pZ zZFUZP1%lTKj_aEflX*vYbLySE4QS zAtX)#lBBW*l10eJ)Bue*1N46}%uipXfi^^6r}q#xVX!6;ZHkT}_sErz(yZY4KvDUxZzjkT$67WvmKoalMJQ6?2BV?8qEC>-SfTS(5 zW#tn5*P~c+qfeZTxxue3?wu}f(E;b!xZaSrp4_pzmYqw^mBhx`)%2G2wH?a#v$4Bl zE8Z=t^U0Cpzo8!~kGZ@(yh(*Y5r)Mo`(LUQF?)Z8NoA*9?}flacU|snNR6!9b>JFd z%0S0K#w}>g>cJBa$^D)~DKo!ohikqzaEzy*FUO5eiTR9xj>QtvnQO+S8orO~E#hn{ z4QZPZq@*azMrsr34^`__2Gw4kn1SBu|n=@;yS;|$ZA80=K$squc zs!sRv&83^`LmRUN=b79fI>h0-!;9G!VT(G3u#vkX8-2NvEvow)z=C&!+mt6ydA6vw zowm*m^-&=2?a$la+N9o+I8YfYznK6*?-_LJdw&AzVW(XGjTJ|`sTq8^FJ6GisDIMt zIcs>Ntl;UQV%L8P~ZzPZ{ckTPen(^YtK6nMgegd^|fmk3lf@f~w zGX9BOBLTWJyl!_)XryTsj(u{1g6=r(-q&r;c+10z7hOe6drXP_Zyd!@%`k0*m~Ma- z+H2T=@-`#F?IG!Pz;tKnQYd!hQV*h8CrS;W?yTx9I8wR*d_zdO1hcS&Voy2)fFp|k zrBEbu&SLP}GEqquB5^sL(k*az7ZU+8qDGHSi;9qxrE{Q2XT%iTjf#;aQXkU)HquY& z6c`hg&Lhg32>b|uT$3F!NkA`#;-;hkNFWXJ9{`|>ta;mhEN?%y9xm8V!hL3DOTpO% zxpMTC&(D8)eq+3Fv@bWfMYZpE+Si6wht_Xws0Git+~B>jZ;P}a``q@a?U#-h7(sU} zITq=~?2f%@v)%s>yZQucf9Q^gj zuSW9r-c734NJ6M3Xq(S?0EXwl6{y)pv26g3F8aG+ehwUo?swx3H>!i-g!`DG$S5H% ztrwGNKARBUmWDwAN^p4wz%Gg7_`fjc6TCUswv9LE@#X^VenGs3PvLtcMtF9O>FzZg eBU&mXo)a{Fp(FsaT~{sJj-NHy&k2|rcKi<=-$o|@ diff --git a/westech_r2/westech_r2/__init__.py b/westech_r2/page/receiving/__init__.py similarity index 100% rename from westech_r2/westech_r2/__init__.py rename to westech_r2/page/receiving/__init__.py diff --git a/westech_r2/page/receiving/receiving.js b/westech_r2/page/receiving/receiving.js new file mode 100644 index 0000000..3761266 --- /dev/null +++ b/westech_r2/page/receiving/receiving.js @@ -0,0 +1,598 @@ +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(); +}; diff --git a/westech_r2/westech_r2/page/r2_tracking/r2_tracking.json b/westech_r2/page/receiving/receiving.json similarity index 60% rename from westech_r2/westech_r2/page/r2_tracking/r2_tracking.json rename to westech_r2/page/receiving/receiving.json index cbedb3f..f2f953a 100644 --- a/westech_r2/westech_r2/page/r2_tracking/r2_tracking.json +++ b/westech_r2/page/receiving/receiving.json @@ -1,15 +1,16 @@ { "content": null, - "creation": "2026-05-09 14:00:00", + "creation": "2026-05-20 21:24:08.575605", "docstatus": 0, "doctype": "Page", + "icon": "stock", "idx": 0, - "modified": "2026-05-09 15:09:48.707863", + "modified": "2026-05-20 21:27:35.049633", "modified_by": "Administrator", "module": "Westech R2", - "name": "r2-tracking", + "name": "receiving", "owner": "Administrator", - "page_name": "r2-tracking", + "page_name": "receiving", "roles": [ { "role": "All" @@ -19,5 +20,5 @@ "standard": "Yes", "style": null, "system_page": 0, - "title": "R2 Data Tracking" + "title": "Receiving" } \ No newline at end of file diff --git a/westech_r2/westech_r2/page/intake/intake.py b/westech_r2/page/receiving/receiving.py similarity index 66% rename from westech_r2/westech_r2/page/intake/intake.py rename to westech_r2/page/receiving/receiving.py index 55cf5c6..f5ae77a 100644 --- a/westech_r2/westech_r2/page/intake/intake.py +++ b/westech_r2/page/receiving/receiving.py @@ -2,4 +2,4 @@ from frappe import _ def get_context(context): context.no_cache = 1 - context.title = _("Intake Station") \ No newline at end of file + context.title = _("Receiving") diff --git a/westech_r2/page/route_planner/__pycache__/__init__.cpython-312.pyc b/westech_r2/page/route_planner/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index d86b09dcf73e9e7b42d73ddb732e28c23cb8c225..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181 zcmX@j%ge<81Xam=Ss?l`h(HIQS%4zb87dhx8U0o=6fpsLpFwJVh3IGG=cekX6(trF zr0S;@738H>l;|d<<|Sw71BHt9%TtR>Qj;^{i;R$&1&QgY`bGJrC8_ZRIf;3BsYUwn r@tJv5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!^3l)8&rQ`&D@rUV zNYzg*D#%N%DA7$y%}dVE2MQJIm!}q&q$X#?7a8GT#>Z#oWtPOp>lIY~;;_lhPbtkw YwJTx;8qNsB#URE -
-
-

Customer Intake

-
-
-
-
- - -
-
-
- -
-
-
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
-
-
- - - -
-
- diff --git a/westech_r2/westech_r2/page/customer_intake/customer_intake.js b/westech_r2/westech_r2/page/customer_intake/customer_intake.js deleted file mode 100644 index 4952da8..0000000 --- a/westech_r2/westech_r2/page/customer_intake/customer_intake.js +++ /dev/null @@ -1,125 +0,0 @@ -frappe.pages["customer-intake"].on_page_load = function(wrapper) { - var page = frappe.ui.make_app_page({parent: wrapper, title: __("Customer Intake"), single_column: true}); - var content = frappe.render_template("customer-intake", {}); - $(page.body).append(content); - - var currentCustomer = null; - var searchTimer = null; - - function clearForm() { - currentCustomer = null; - $("#cust-name, #cust-number, #cust-phone, #cust-address, #cust-city, #cust-state, #cust-zip, #cust-contacts, #cust-email, #cust-hours").val(""); - $("#search-results").empty(); - $("#no-match").hide(); - $("#cust-status").text(""); - } - - function fillForm(c) { - currentCustomer = c; - $("#cust-name").val(c.customer_name || ""); - $("#cust-number").val(c.customer_number || ""); - $("#cust-phone").val(c.phone || c.address_phone || ""); - $("#cust-address").val(c.address_line1 || ""); - $("#cust-city").val(c.city || ""); - $("#cust-state").val(c.state || ""); - $("#cust-zip").val(c.pincode || ""); - $("#cust-contacts").val(c.contact_persons || ""); - $("#cust-email").val(c.email_id || ""); - $("#cust-hours").val(c.hours_of_operation || ""); - $("#cust-status").text("Selected: " + (c.customer_name || c.name)); - $("#no-match").hide(); - } - - function doSearch() { - var q = $("#intake-search").val().trim(); - if (q.length < 2) { $("#search-results").empty(); return; } - frappe.call({ - method: "westech_r2.page.customer-intake.customer-intake.search_customers", - args: {q: q}, - callback: function(r) { - var list = $("#search-results").empty(); - if (r.message && r.message.length) { - r.message.forEach(function(c) { - var item = $('
') - .html('' + frappe.utils.escape_html(c.customer_name || c.name) + ' ' + - (c.address_line1 ? '
' + frappe.utils.escape_html(c.address_line1) : '') + - (c.city ? ', ' + c.city : '') + - (c.phone ? '
' + c.phone + '' : '')); - item.on("click", function() { fillForm(c); }); - list.append(item); - }); - $("#no-match").hide(); - } else { - $("#no-match").show(); - } - } - }); - } - - $("#intake-search").on("input", function() { - clearTimeout(searchTimer); - searchTimer = setTimeout(doSearch, 300); - }); - - $("#btn-add-new").click(function() { - clearForm(); - $("#cust-name").val($("#intake-search").val()); - $("#cust-status").text("Adding new customer..."); - }); - - $("#btn-save-cust").click(function() { - var data = { - customer_name: $("#cust-name").val(), - customer_number: $("#cust-number").val(), - phone: $("#cust-phone").val(), - address_line1: $("#cust-address").val(), - city: $("#cust-city").val(), - state: $("#cust-state").val(), - pincode: $("#cust-zip").val(), - contact_persons: $("#cust-contacts").val(), - email_id: $("#cust-email").val(), - hours_of_operation: $("#cust-hours").val() - }; - if (currentCustomer && currentCustomer.name) { - data.name = currentCustomer.name; - } - frappe.call({ - method: "westech_r2.page.customer-intake.customer-intake.create_customer_from_intake", - args: {data: JSON.stringify(data)}, - callback: function(r) { - if (r.message && r.message.status === "ok") { - currentCustomer = {name: r.message.customer, customer_name: data.customer_name}; - $("#cust-status").text("Saved: " + r.message.customer); - frappe.show_alert("Customer saved", 3); - } else { - frappe.show_alert("Error saving customer", 5); - } - } - }); - }); - - $("#btn-create-pallet").click(function() { - if (!currentCustomer || !currentCustomer.name) { - frappe.msgprint("Please select or save a customer first."); - return; - } - frappe.call({ - method: "westech_r2.page.customer-intake.customer-intake.create_pallet", - args: { - data: JSON.stringify({ - customer: currentCustomer.name, - customer_number: $("#cust-number").val(), - data_status: $("#pallet-data-status").val(), - status: $("#pallet-status").val(), - inbound_weight: $("#pallet-weight").val() - }) - }, - callback: function(r) { - if (r.message && r.message.status === "ok") { - frappe.msgprint("Pallet created: " + r.message.pallet); - $("#cust-status").text("Pallet: " + r.message.pallet); - } - } - }); - }); -}; diff --git a/westech_r2/westech_r2/page/customer_intake/customer_intake.py b/westech_r2/westech_r2/page/customer_intake/customer_intake.py deleted file mode 100644 index 544f5be..0000000 --- a/westech_r2/westech_r2/page/customer_intake/customer_intake.py +++ /dev/null @@ -1,84 +0,0 @@ -import frappe -from frappe import _ - -@frappe.whitelist() -def search_customers(q=""): - if not q or len(q) < 2: - return [] - q = q.strip().lower() - customers = frappe.db.sql(""" - SELECT c.name, c.customer_name, c.customer_number, c.phone, c.email_id, - a.address_line1, a.city, a.state, a.pincode - FROM tabCustomer c - LEFT JOIN tabDynamic Link dl ON dl.link_doctype = 'Customer' AND dl.link_name = c.name AND dl.parenttype = 'Address' - LEFT JOIN tabAddress a ON a.name = dl.parent - WHERE LOWER(c.customer_name) LIKE %s OR LOWER(c.customer_number) LIKE %s OR LOWER(c.phone) LIKE %s - ORDER BY c.customer_name - LIMIT 20 - """, ("%" + q + "%", "%" + q + "%", "%" + q + "%"), as_dict=True) - return customers - -@frappe.whitelist() -def get_customer(name): - if not name: - return {} - cust = frappe.get_doc("Customer", name) - result = cust.as_dict() - addr = frappe.db.sql(""" - SELECT a.address_line1, a.city, a.state, a.pincode, a.phone - FROM tabAddress a - JOIN tabDynamic Link dl ON dl.parent = a.name AND dl.link_doctype = 'Customer' AND dl.link_name = %s - LIMIT 1 - """, (name,), as_dict=True) - if addr: - result.update({ - "address_line1": addr[0].address_line1, - "city": addr[0].city, - "state": addr[0].state, - "pincode": addr[0].pincode, - "address_phone": addr[0].phone - }) - return result - -@frappe.whitelist() -def create_customer_from_intake(data): - data = frappe.parse_json(data) - if not data.get("customer_name"): - frappe.throw(_("Customer name required")) - customer = frappe.new_doc("Customer") - customer.customer_name = data.get("customer_name") - customer.customer_group = data.get("customer_group", "IT Recycling") - customer.customer_type = "Company" - customer.customer_number = data.get("customer_number") - customer.phone = data.get("phone") - customer.email_id = data.get("email_id") - customer.legacy_notes = data.get("legacy_notes") - customer.hours_of_operation = data.get("hours_of_operation") - customer.contact_persons = data.get("contact_persons") - customer.save() - if data.get("address_line1") or data.get("city"): - addr = frappe.new_doc("Address") - addr.address_title = customer.customer_name - addr.address_type = "Billing" - addr.address_line1 = data.get("address_line1", "Unknown") - addr.city = data.get("city", "Unknown") - addr.state = data.get("state", "") - addr.pincode = data.get("pincode", "") - addr.country = "United States" - addr.append("links", {"link_doctype": "Customer", "link_name": customer.name}) - addr.save() - return {"status": "ok", "customer": customer.name} - -@frappe.whitelist() -def create_pallet(data): - data = frappe.parse_json(data) - if not data.get("customer"): - frappe.throw(_("Customer required")) - pallet = frappe.new_doc("Pallet") - pallet.customer = data.get("customer") - pallet.customer_number = data.get("customer_number") - pallet.data_status = data.get("data_status", "D0") - pallet.status = data.get("status", "Received") - pallet.inbound_weight = data.get("inbound_weight", "") - pallet.save() - return {"status": "ok", "pallet": pallet.name} diff --git a/westech_r2/westech_r2/page/customer_records/__init__.py b/westech_r2/westech_r2/page/customer_records/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/westech_r2/westech_r2/page/customer_records/customer-records.json b/westech_r2/westech_r2/page/customer_records/customer-records.json deleted file mode 100644 index 1fbb98a..0000000 --- a/westech_r2/westech_r2/page/customer_records/customer-records.json +++ /dev/null @@ -1 +0,0 @@ -{"content": null,"creation": "2026-05-20 22:00:00.000000","docstatus": 0,"doctype": "Page","idx": 0,"modified": "2026-05-20 22:00:00.000000","modified_by": "Administrator","module": "Westech R2","name": "customer-records","owner": "Administrator","page_name": "customer-records","roles": [{"doctype": "Has Role","idx": 1,"name": "a80mopj93i","parent": "customer-records","parentfield": "roles","parenttype": "Page","role": "All"}],"script": null,"standard": "Yes","style": null,"system_page": 0,"title": "Customer Records"} diff --git a/westech_r2/westech_r2/page/customer_records/customer_records.html b/westech_r2/westech_r2/page/customer_records/customer_records.html deleted file mode 100644 index fd9d8f2..0000000 --- a/westech_r2/westech_r2/page/customer_records/customer_records.html +++ /dev/null @@ -1,227 +0,0 @@ - - -
-

Modify Records

- -
- - - 0 of 0 - - - - - - - -
- -
-
- - -
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
- - -
diff --git a/westech_r2/westech_r2/page/customer_records/customer_records.js b/westech_r2/westech_r2/page/customer_records/customer_records.js deleted file mode 100644 index 2bef1e7..0000000 --- a/westech_r2/westech_r2/page/customer_records/customer_records.js +++ /dev/null @@ -1,7 +0,0 @@ -frappe.pages['customer-records'].on_page_load = function(wrapper) { - var page = frappe.ui.make_app_page({ - parent: wrapper, - title: 'Customer Records', - single_column: true - }); -} \ No newline at end of file diff --git a/westech_r2/westech_r2/page/customer_records/customer_records.json b/westech_r2/westech_r2/page/customer_records/customer_records.json deleted file mode 100644 index d89235b..0000000 --- a/westech_r2/westech_r2/page/customer_records/customer_records.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "content": null, - "creation": "2026-05-20 15:03:29.017530", - "docstatus": 0, - "doctype": "Page", - "idx": 0, - "modified": "2026-05-20 15:03:29.017530", - "modified_by": "Administrator", - "module": "Westech R2", - "name": "customer-records", - "owner": "Administrator", - "page_name": "customer-records", - "roles": [ - { - "role": "All" - } - ], - "script": null, - "standard": "Yes", - "style": null, - "system_page": 0, - "title": "Customer Records" -} \ No newline at end of file diff --git a/westech_r2/westech_r2/page/customer_records/customer_records.py b/westech_r2/westech_r2/page/customer_records/customer_records.py deleted file mode 100644 index 008840e..0000000 --- a/westech_r2/westech_r2/page/customer_records/customer_records.py +++ /dev/null @@ -1,85 +0,0 @@ -import frappe - -@frappe.whitelist() -def get_records(): - # For now return Lead records mapped to form fields - leads = frappe.db.sql(""" - SELECT name, company_name, email_id, mobile_no, phone, address_line1, city, state, pincode, - title, status, industry, source - FROM tabLead - ORDER BY creation DESC - LIMIT 1000 - """, as_dict=True) - result = [] - for l in leads: - result.append({ - "name": l.name, - "additional_numbers": "", - "field_star": l.status or "", - "customer_address": l.address_line1 or "", - "any_letter": l.title or "", - "city": l.city or "", - "field_e": l.email_id or "", - "zip": l.pincode or "", - "company_name": l.company_name or "", - "contacted_date": "", - "contact_persons": l.title or "", - "follow_up_date": "", - "email_address": l.email_id or "", - "last_pu_date": "", - "contact_numbers": (l.phone or "") + "\n" + (l.mobile_no or ""), - "hours_operation": l.industry or "", - "comments": l.source or "" - }) - return result - -@frappe.whitelist() -def save_record(data): - data = frappe.parse_json(data) - # For now just return OK - Lead update can be wired later - return {"status": "ok", "message": "Saved " + (data.get("name") or "")} - -@frappe.whitelist() -def delete_record(name): - # For now return OK - return {"status": "ok", "message": "Deleted " + name} - -@frappe.whitelist() -def search_records(field, value): - leads = frappe.db.sql(""" - SELECT name, company_name, email_id, mobile_no, phone, address_line1, city, state, pincode, - title, status, industry, source - FROM tabLead - WHERE LOWER(company_name) LIKE %s - OR LOWER(title) LIKE %s - OR LOWER(address_line1) LIKE %s - OR LOWER(city) LIKE %s - OR LOWER(pincode) LIKE %s - OR LOWER(email_id) LIKE %s - OR LOWER(phone) LIKE %s - OR LOWER(mobile_no) LIKE %s - ORDER BY creation DESC - LIMIT 100 - """, tuple(["%" + value + "%"] * 8), as_dict=True) - result = [] - for l in leads: - result.append({ - "name": l.name, - "additional_numbers": "", - "field_star": l.status or "", - "customer_address": l.address_line1 or "", - "any_letter": l.title or "", - "city": l.city or "", - "field_e": l.email_id or "", - "zip": l.pincode or "", - "company_name": l.company_name or "", - "contacted_date": "", - "contact_persons": l.title or "", - "follow_up_date": "", - "email_address": l.email_id or "", - "last_pu_date": "", - "contact_numbers": (l.phone or "") + "\n" + (l.mobile_no or ""), - "hours_operation": l.industry or "", - "comments": l.source or "" - }) - return result diff --git a/westech_r2/westech_r2/page/ebay-pricing/__init__.py b/westech_r2/westech_r2/page/ebay-pricing/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.css b/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.css deleted file mode 100644 index 8b27534..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.css +++ /dev/null @@ -1,5 +0,0 @@ -.badge-fresh { background-color: #28a745; } -.badge-aging { background-color: #ffc107; color: #212529; } -.badge-expired { background-color: #dc3545; } -.badge-needs { background-color: #fd7e14; } -.badge-error { background-color: #6c757d; } diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.html b/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.html deleted file mode 100644 index 4015bfb..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.html +++ /dev/null @@ -1,8 +0,0 @@ - -
diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.js b/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.js deleted file mode 100644 index 1414078..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.js +++ /dev/null @@ -1,323 +0,0 @@ -frappe.pages['ebay-pricing'].on_page_load = function(wrapper) { - var page = frappe.ui.make_app_page({ - parent: wrapper, - title: __('eBay Pricing'), - single_column: true - }); - - let $container = $(` -
-
-
-
- -
- - - - -
-
-
-
-
- - - -
-
-
-
-
-
-

Apply Pricing to Inventory

-
- -
- - -
-
-
-
Pricing Status
-
-

Click Apply All to see stats

-
-
-
-
-
-
-
- -

Search for a model or run batch pricing

-
-
-
`).appendTo(page.main); - - // Load item dropdown - load_item_dropdown(); - - // Event handlers - $container.find('#ebay-search-btn').on('click', function() { - let query = $container.find('#ebay-search-input').val().trim(); - if (!query) { - frappe.msgprint(__('Enter a model to search')); - return; - } - search_ebay(query); - }); - - $container.find('#ebay-search-input').on('keypress', function(e) { - if (e.which === 13) { - $container.find('#ebay-search-btn').trigger('click'); - } - }); - - $container.find('#ebay-batch-btn').on('click', function() { - let size = $container.find('#ebay-batch-size').val(); - run_batch(size); - }); - - $container.find('#ebay-apply-btn').on('click', function() { - let item = $container.find('#ebay-apply-item').val(); - if (!item) { - frappe.msgprint(__('Select an Item first')); - return; - } - apply_pricing(item); - }); - - $container.find('#ebay-apply-all-btn').on('click', function() { - apply_pricing_all(); - }); - - function load_item_dropdown() { - frappe.call({ - method: 'frappe.client.get_list', - args: { - doctype: 'Item', - filters: { - 'disabled': 0, - 'item_group': ['in', ['Laptop', 'Desktop', 'Tablet', 'Phone', 'Workstation']] - }, - fields: ['name', 'item_name'], - limit: 1000 - }, - callback: function(r) { - if (r.message) { - let $select = $container.find('#ebay-apply-item'); - r.message.forEach(item => { - $select.append(``); - }); - } - } - }); - } - - function search_ebay(query) { - frappe.call({ - method: 'westech_r2.api.ebay_pricing.search_model', - args: { query: query }, - freeze: true, - freeze_message: __('Searching eBay sold listings...'), - callback: function(r) { - if (r.message && r.message.results) { - render_results(r.message); - } else { - let msg = (r.message && r.message.message) || __('No results found'); - frappe.msgprint(msg); - } - } - }); - } - - function run_batch(size) { - frappe.call({ - method: 'westech_r2.api.ebay_pricing.run_batch', - args: { batch_size: size }, - freeze: true, - freeze_message: __('Running batch pricing...'), - callback: function(r) { - if (r.message) { - frappe.msgprint(__('Batch complete: {0} priced, {1} failed, {2} skipped', - [r.message.priced, r.message.failed, r.message.skipped])); - load_recent_pricing(); - } - } - }); - } - - function apply_pricing(item_code) { - frappe.call({ - method: 'westech_r2.api.ebay_pricing.batch_apply_pricing', - args: { item_code: item_code }, - freeze: true, - freeze_message: __('Applying pricing to Serial Nos...'), - callback: function(r) { - if (r.message) { - render_pricing_stats(r.message); - frappe.msgprint(__('Pricing applied: {0} priced, {1} commodity, {2} needs grading', - [r.message.priced, r.message.commodity, r.message.needs_grading])); - } - } - }); - } - - function apply_pricing_all() { - frappe.call({ - method: 'westech_r2.api.ebay_pricing.batch_apply_pricing', - args: { batch_size: 1000 }, - freeze: true, - freeze_message: __('Applying pricing to all Serial Nos...'), - callback: function(r) { - if (r.message) { - render_pricing_stats(r.message); - frappe.msgprint(__('Batch pricing applied: {0} priced, {1} commodity, {2} needs grading, {3} errors', - [r.message.priced, r.message.commodity, r.message.needs_grading, r.message.errors])); - } - } - }); - } - - function render_pricing_stats(stats) { - let html = ` - - - - - - -
Priced${stats.priced || 0}
Commodity${stats.commodity || 0}
Needs Grading${stats.needs_grading || 0}
Needs Price Point${stats.needs_price_point || 0}
Errors${stats.errors || 0}
- `; - $container.find('#pricing-stats').html(html); - } - - function render_results(data) { - let $area = $container.find('#ebay-results-area').empty(); - if (!data.results || !data.results.length) { - $area.html(`
No results
`); - return; - } - - let html = ` - - - - - - - - `; - data.results.forEach(item => { - html += ` - - - - - - `; - }); - html += `
TitlePriceConditionSoldShipping
${frappe.utils.escape_html(item.title || '')}$${(item.price || 0).toFixed(2)}${frappe.utils.escape_html(item.condition || '')}${item.sold || ''}${item.shipping || ''}
`; - - if (data.pricing) { - html += `
-

Pricing Summary

-
-
Low: $${data.pricing.price_low}
-
High: $${data.pricing.price_high}
-
Average: $${data.pricing.price_average}
-
Median: $${data.pricing.price_auction}
-
-
-
Source: ${data.pricing.source}
-
Samples: ${data.pricing.sample_count}
-
-
`; - } - - $area.html(html); - } - - function load_recent_pricing() { - frappe.call({ - method: 'westech_r2.api.ebay_pricing.get_recent_pricing', - args: { limit: 50 }, - callback: function(r) { - if (r.message) { - render_pricing_grid(r.message); - } - } - }); - } - - function render_pricing_grid(items) { - let $area = $container.find('#ebay-results-area'); - if (!items || !items.length) { - $area.html(`
No pricing data yet
`); - return; - } - - let html = `

Recent Pricing Results

- - - - - - - - - - - - - - `; - - items.forEach(row => { - let status_class = 'badge-needs'; - if (row.pricing_status === 'Priced') status_class = 'badge-fresh'; - else if (row.pricing_status === 'Manual Override') status_class = 'badge-fresh'; - else if (row.pricing_status === 'Expired') status_class = 'badge-expired'; - else if (row.pricing_status === 'Error') status_class = 'badge-error'; - - let age = row.days_since_pricing || 0; - let age_badge = age < 90 ? 'badge-fresh' : (age < 120 ? 'badge-aging' : 'badge-expired'); - - html += ` - - - - - - - - - - - `; - }); - - html += `
ManufacturerModelStatusAgeLowHighAvgSamplesSourceLast Priced
${frappe.utils.escape_html(row.manufacturer || '')}${frappe.utils.escape_html(row.model || '')}${row.pricing_status}${age} days$${row.price_low || ''}$${row.price_high || ''}$${row.price_average || ''}${row.sample_count || ''}${row.source || ''}${frappe.datetime.str_to_user(row.scraped_at) || ''}
`; - $area.html(html); - } - - load_recent_pricing(); -}; diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.json b/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.json deleted file mode 100644 index ab9d81b..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "creation": "2026-05-17 05:30:00.000000", - "docstatus": 0, - "doctype": "Page", - "icon": "fa fa-tags", - "modified": "2026-05-17 05:30:00.000000", - "modified_by": "Administrator", - "module": "Westech R2", - "name": "ebay-pricing", - "owner": "Administrator", - "page_name": "ebay-pricing", - "roles": [ - { - "role": "System Manager" - }, - { - "role": "Stock User" - }, - { - "role": "Sales User" - } - ], - "standard": "Yes", - "system_page": 0, - "title": "eBay Pricing" -} \ No newline at end of file diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.py b/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.py deleted file mode 100644 index 4053179..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay-pricing.py +++ /dev/null @@ -1 +0,0 @@ -# eBay Pricing desk page diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.css b/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.css deleted file mode 100644 index 8b27534..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.css +++ /dev/null @@ -1,5 +0,0 @@ -.badge-fresh { background-color: #28a745; } -.badge-aging { background-color: #ffc107; color: #212529; } -.badge-expired { background-color: #dc3545; } -.badge-needs { background-color: #fd7e14; } -.badge-error { background-color: #6c757d; } diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.html b/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.html deleted file mode 100644 index 4015bfb..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.html +++ /dev/null @@ -1,8 +0,0 @@ - -
diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.js b/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.js deleted file mode 120000 index 8cd44b0..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.js +++ /dev/null @@ -1 +0,0 @@ -ebay-pricing.js \ No newline at end of file diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.json b/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.json deleted file mode 120000 index d388217..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.json +++ /dev/null @@ -1 +0,0 @@ -ebay-pricing.json \ No newline at end of file diff --git a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.py b/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.py deleted file mode 100644 index 4053179..0000000 --- a/westech_r2/westech_r2/page/ebay-pricing/ebay_pricing.py +++ /dev/null @@ -1 +0,0 @@ -# eBay Pricing desk page diff --git a/westech_r2/westech_r2/page/eim_portal/__init__.py b/westech_r2/westech_r2/page/eim_portal/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/westech_r2/westech_r2/page/eim_portal/eim-portal.css b/westech_r2/westech_r2/page/eim_portal/eim-portal.css deleted file mode 100644 index 8b13789..0000000 --- a/westech_r2/westech_r2/page/eim_portal/eim-portal.css +++ /dev/null @@ -1 +0,0 @@ - diff --git a/westech_r2/westech_r2/page/eim_portal/eim-portal.js b/westech_r2/westech_r2/page/eim_portal/eim-portal.js deleted file mode 100644 index 9d84ac1..0000000 --- a/westech_r2/westech_r2/page/eim_portal/eim-portal.js +++ /dev/null @@ -1,4 +0,0 @@ -frappe.pages["eim-portal"].on_page_load = function(wrapper) { - wrapper.innerHTML = '

Redirecting to EIM Device Portal...

'; - setTimeout(function() { window.location.href = "https://eim.diagalon.com"; }, 500); -}; diff --git a/westech_r2/westech_r2/page/eim_portal/eim-portal.json b/westech_r2/westech_r2/page/eim_portal/eim-portal.json deleted file mode 100644 index c06f7bd..0000000 --- a/westech_r2/westech_r2/page/eim_portal/eim-portal.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "creation": "2026-05-09 14:00:00", - "docstatus": 0, - "doctype": "Page", - "idx": 0, - "modified": "2026-05-09 14:00:00", - "modified_by": "Administrator", - "module": "Westech R2", - "name": "eim-portal", - "owner": "Administrator", - "standard": "Yes", - "title": "EIM Device Portal" -} diff --git a/westech_r2/westech_r2/page/eim_portal/eim-portal.py b/westech_r2/westech_r2/page/eim_portal/eim-portal.py deleted file mode 100644 index 9d9e209..0000000 --- a/westech_r2/westech_r2/page/eim_portal/eim-portal.py +++ /dev/null @@ -1,5 +0,0 @@ -import frappe - -def get_context(context): - frappe.local.flags.redirect_location = "https://eim.diagalon.com" - raise frappe.Redirect diff --git a/westech_r2/westech_r2/page/eim_portal/eim_portal.js b/westech_r2/westech_r2/page/eim_portal/eim_portal.js deleted file mode 100644 index e5cbdea..0000000 --- a/westech_r2/westech_r2/page/eim_portal/eim_portal.js +++ /dev/null @@ -1,7 +0,0 @@ -frappe.pages['eim-portal'].on_page_load = function(wrapper) { - var page = frappe.ui.make_app_page({ - parent: wrapper, - title: 'EIM Device Portal', - single_column: true - }); -} \ No newline at end of file diff --git a/westech_r2/westech_r2/page/eim_portal/eim_portal.json b/westech_r2/westech_r2/page/eim_portal/eim_portal.json deleted file mode 100644 index 1330152..0000000 --- a/westech_r2/westech_r2/page/eim_portal/eim_portal.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "content": null, - "creation": "2026-05-09 14:00:00", - "docstatus": 0, - "doctype": "Page", - "idx": 0, - "modified": "2026-05-09 15:09:48.653878", - "modified_by": "Administrator", - "module": "Westech R2", - "name": "eim-portal", - "owner": "Administrator", - "page_name": "eim-portal", - "roles": [ - { - "role": "All" - } - ], - "script": null, - "standard": "Yes", - "style": null, - "system_page": 0, - "title": "EIM Device Portal" -} \ No newline at end of file diff --git a/westech_r2/westech_r2/page/intake/__init__.py b/westech_r2/westech_r2/page/intake/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/westech_r2/westech_r2/page/intake/intake.css b/westech_r2/westech_r2/page/intake/intake.css deleted file mode 100644 index c5f45ab..0000000 --- a/westech_r2/westech_r2/page/intake/intake.css +++ /dev/null @@ -1,11 +0,0 @@ -.intake-station .card { border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; } -.intake-station .card-header { padding: 15px; } -.intake-station .card-body { padding: 20px; } -.intake-station .form-group { margin-bottom: 15px; } -.intake-station .form-control { border-radius: 4px; padding: 8px 12px; font-size: 16px; } -.intake-station .form-control:focus { border-color: #6f42c1; box-shadow: 0 0 0 0.2rem rgba(111,66,193,0.25); } -.intake-station label { font-weight: 600; margin-bottom: 4px; } -.intake-station h5 { margin-bottom: 15px; padding-bottom: 8px; border-bottom: 2px solid #e0e0e0; } -.intake-station .table th { background: #f8f9fa; } -.intake-station .btn-primary { background: linear-gradient(135deg, #6f42c1, #28a745) !important; border: none !important; } -.intake-station .label { font-size: 0.85em; } \ No newline at end of file diff --git a/westech_r2/westech_r2/page/intake/intake.js b/westech_r2/westech_r2/page/intake/intake.js deleted file mode 100644 index 0242040..0000000 --- a/westech_r2/westech_r2/page/intake/intake.js +++ /dev/null @@ -1,772 +0,0 @@ -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
-
-
- - - - - - - - - - - - - - - - -
StatusCustomer #DriverReceivedRED/R2ItemsWeightActions
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('