diff --git a/westech_r2.egg-info/SOURCES.txt b/westech_r2.egg-info/SOURCES.txt
index 92808c7..9142f9b 100644
--- a/westech_r2.egg-info/SOURCES.txt
+++ b/westech_r2.egg-info/SOURCES.txt
@@ -8,5 +8,19 @@ westech_r2.egg-info/dependency_links.txt
westech_r2.egg-info/not-zip-safe
westech_r2.egg-info/requires.txt
westech_r2.egg-info/top_level.txt
+westech_r2/api/__init__.py
+westech_r2/api/ebay_pricing.py
+westech_r2/api/install_ssh.py
+westech_r2/api/optimize_routes.py
+westech_r2/api/scoring.py
westech_r2/config/__init__.py
-westech_r2/config/desktop.py
\ No newline at end of file
+westech_r2/config/desktop.py
+westech_r2/doctype/__init__.py
+westech_r2/doctype/load/__init__.py
+westech_r2/doctype/load/load.py
+westech_r2/doctype/pallet/__init__.py
+westech_r2/doctype/pallet/pallet.py
+westech_r2/doctype/scheduled_pickup/__init__.py
+westech_r2/doctype/scheduled_pickup/scheduled_pickup.py
+westech_r2/public/__init__.py
+westech_r2/westech_r2/__init__.py
\ No newline at end of file
diff --git a/westech_r2/westech_r2/page/sales_manager/sales-manager.html b/westech_r2/westech_r2/page/sales-manager/sales-manager.html
similarity index 100%
rename from westech_r2/westech_r2/page/sales_manager/sales-manager.html
rename to westech_r2/westech_r2/page/sales-manager/sales-manager.html
diff --git a/westech_r2/westech_r2/page/sales_manager/sales-manager.js b/westech_r2/westech_r2/page/sales-manager/sales-manager.js
similarity index 52%
rename from westech_r2/westech_r2/page/sales_manager/sales-manager.js
rename to westech_r2/westech_r2/page/sales-manager/sales-manager.js
index ec9cd1e..af0bbc7 100644
--- a/westech_r2/westech_r2/page/sales_manager/sales-manager.js
+++ b/westech_r2/westech_r2/page/sales-manager/sales-manager.js
@@ -6,7 +6,93 @@ frappe.pages['sales-manager'].on_page_load = function(wrapper) {
});
var $content = $(wrapper).find('.page-content');
- $content.html(frappe.render_template('sales_manager'));
+ $content.empty();
+
+ // Build the page HTML directly
+ $content.html(`
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Serial |
+ Item |
+ Grade |
+ Specs |
+ Score |
+ Tier |
+ Market Range |
+ Suggested |
+ Age |
+ Final Price |
+ Actions |
+
+
+
+
+
+
+
+
+ `);
loadPricingData();
@@ -60,13 +146,10 @@ function renderPricingTable(serials) {
serials.forEach(function(s) {
var ageBadge = '' +
(s.age.status || 'unknown') + '';
-
var tierBadge = '' + (s.tier || 'N/A') + '';
-
var specs = [];
if (s.processor) specs.push(s.processor);
if (s.ram) specs.push(s.ram);
-
var marketRange = '$' + (s.market.low || 0).toFixed(0) + ' - $' + (s.market.high || 0).toFixed(0);
var row = '' +
@@ -81,11 +164,8 @@ function renderPricingTable(serials) {
'' + ageBadge + ' ' + s.age.days + 'd | ' +
' | ' +
- '' +
- '' +
- ' | ' +
+ ' | ' +
'
';
-
tbody.append(row);
});
diff --git a/westech_r2/westech_r2/page/sales_manager/sales-manager.json b/westech_r2/westech_r2/page/sales-manager/sales-manager.json
similarity index 100%
rename from westech_r2/westech_r2/page/sales_manager/sales-manager.json
rename to westech_r2/westech_r2/page/sales-manager/sales-manager.json
diff --git a/westech_r2/westech_r2/page/sales-manager/sales_manager.py b/westech_r2/westech_r2/page/sales-manager/sales_manager.py
new file mode 100644
index 0000000..d2aad02
--- /dev/null
+++ b/westech_r2/westech_r2/page/sales-manager/sales_manager.py
@@ -0,0 +1,6 @@
+import frappe
+from frappe import _
+
+def get_context(context):
+ context.no_cache = 1
+ return context
diff --git a/westech_r2/westech_r2/page/sales_manager/sales_manager.html b/westech_r2/westech_r2/page/sales_manager/sales_manager.html
new file mode 100644
index 0000000..e37f258
--- /dev/null
+++ b/westech_r2/westech_r2/page/sales_manager/sales_manager.html
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Serial |
+ Item |
+ Grade |
+ Specs |
+ Score |
+ Tier |
+ Market Range |
+ Suggested |
+ Age |
+ Final Price |
+ Actions |
+
+
+
+
+
+
+
+
+
diff --git a/westech_r2/westech_r2/page/sales_manager/sales_manager.js b/westech_r2/westech_r2/page/sales_manager/sales_manager.js
new file mode 100644
index 0000000..460ba8c
--- /dev/null
+++ b/westech_r2/westech_r2/page/sales_manager/sales_manager.js
@@ -0,0 +1,205 @@
+frappe.pages['sales_manager'].on_page_load = function(wrapper) {
+ var page = frappe.ui.make_app_page({
+ parent: wrapper,
+ title: 'Sales Manager',
+ single_column: true
+ });
+
+ var $content = $(wrapper).find('.page-content');
+ $content.empty();
+
+ // Build the page HTML directly
+ $content.html(`
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Serial |
+ Item |
+ Grade |
+ Specs |
+ Score |
+ Tier |
+ Market Range |
+ Suggested |
+ Age |
+ Final Price |
+ Actions |
+
+
+
+
+
+
+
+
+ `);
+
+ loadPricingData();
+
+ $('#btn-refresh').on('click', function() {
+ loadPricingData();
+ });
+
+ $('#btn-batch-score').on('click', function() {
+ frappe.confirm('Calculate scores for all serials?', function() {
+ frappe.call({
+ method: 'westech_r2.api.scoring.batch_calculate_scores',
+ args: {batch_size: 500},
+ callback: function(r) {
+ if (r.message) {
+ frappe.msgprint('Updated: ' + r.message.updated +
+ ', Scrap: ' + r.message.scrap +
+ ', Errors: ' + r.message.errors);
+ loadPricingData();
+ }
+ }
+ });
+ });
+ });
+
+ $('#btn-all, #btn-current, #btn-stale, #btn-aging, #btn-expired').on('click', function() {
+ var filter = $(this).attr('id').replace('btn-', '');
+ filterTable(filter);
+ });
+};
+
+function loadPricingData() {
+ $('#loading').show();
+ $('#pricing-tbody').empty();
+
+ frappe.call({
+ method: 'westech_r2.api.scoring.get_sales_pricing_data',
+ args: {limit: 100},
+ callback: function(r) {
+ $('#loading').hide();
+ if (r.message && r.message.serials) {
+ renderPricingTable(r.message.serials);
+ }
+ }
+ });
+}
+
+function renderPricingTable(serials) {
+ var tbody = $('#pricing-tbody');
+ tbody.empty();
+
+ serials.forEach(function(s) {
+ var ageBadge = '' +
+ (s.age.status || 'unknown') + '';
+ var tierBadge = '' + (s.tier || 'N/A') + '';
+ var specs = [];
+ if (s.processor) specs.push(s.processor);
+ if (s.ram) specs.push(s.ram);
+ var marketRange = '$' + (s.market.low || 0).toFixed(0) + ' - $' + (s.market.high || 0).toFixed(0);
+
+ var row = '' +
+ '| ' + s.serial_no + ' | ' +
+ '' + (s.item_name || s.item_code) + ' | ' +
+ '' + (s.cosmetic_grade || '') + ' | ' +
+ '' + specs.join(' / ') + ' | ' +
+ '' + (s.score || 0) + ' | ' +
+ '' + tierBadge + ' | ' +
+ '' + marketRange + ' | ' +
+ '$' + (s.suggested_price || 0).toFixed(2) + ' | ' +
+ '' + ageBadge + ' ' + s.age.days + 'd | ' +
+ ' | ' +
+ ' | ' +
+ '
';
+ tbody.append(row);
+ });
+
+ $('.btn-save-price').on('click', function() {
+ var serial = $(this).data('serial');
+ var price = $(this).closest('tr').find('.final-price').val();
+ saveFinalPrice(serial, price);
+ });
+}
+
+function filterTable(filter) {
+ if (filter === 'all') {
+ $('#pricing-tbody tr').show();
+ } else {
+ $('#pricing-tbody tr').hide();
+ $('#pricing-tbody tr[data-age="' + filter + '"]').show();
+ }
+}
+
+function saveFinalPrice(serial_no, price) {
+ frappe.call({
+ method: 'frappe.client.set_value',
+ args: {
+ doctype: 'Serial No',
+ name: serial_no,
+ fieldname: {
+ 'assigned_price': price,
+ 'pricing_status': 'Manual Override'
+ }
+ },
+ callback: function(r) {
+ if (!r.exc) {
+ frappe.show_alert({message: 'Price saved for ' + serial_no, indicator: 'green'});
+ }
+ }
+ });
+}
diff --git a/westech_r2/westech_r2/page/sales_manager/sales_manager.json b/westech_r2/westech_r2/page/sales_manager/sales_manager.json
new file mode 100644
index 0000000..1dc08a6
--- /dev/null
+++ b/westech_r2/westech_r2/page/sales_manager/sales_manager.json
@@ -0,0 +1,12 @@
+{
+ "title": "Sales Manager",
+ "route": "sales-manager",
+ "icon": "fa fa-dollar-sign",
+ "roles": [
+ {"role": "System Manager"},
+ {"role": "Sales User"}
+ ],
+ "standard": "Yes",
+ "type": "page",
+ "module": "Westech R2"
+}