fix: correct page directory structure
This commit is contained in:
@@ -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
|
||||
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
|
||||
+88
-8
@@ -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(`
|
||||
<div class="container-fluid">
|
||||
<div class="page-header">
|
||||
<h2>Sales Manager — Pricing Queue</h2>
|
||||
<p class="text-muted">Review suggested prices, adjust tiers, and set final prices</p>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-success">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Current</h5>
|
||||
<p class="card-text">0-30 days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-dark bg-warning">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Stale</h5>
|
||||
<p class="card-text">30-60 days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white" style="background-color: #fd7e14;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Aging</h5>
|
||||
<p class="card-text">60-90 days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-danger">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Expired</h5>
|
||||
<p class="card-text">90+ days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-outline-primary" id="btn-all">All</button>
|
||||
<button type="button" class="btn btn-outline-success" id="btn-current">Current</button>
|
||||
<button type="button" class="btn btn-outline-warning" id="btn-stale">Stale</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="btn-aging">Aging</button>
|
||||
<button type="button" class="btn btn-outline-danger" id="btn-expired">Expired</button>
|
||||
</div>
|
||||
<button class="btn btn-primary float-right" id="btn-refresh">Refresh</button>
|
||||
<button class="btn btn-secondary float-right mr-2" id="btn-batch-score">Batch Score</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover" id="pricing-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Serial</th>
|
||||
<th>Item</th>
|
||||
<th>Grade</th>
|
||||
<th>Specs</th>
|
||||
<th>Score</th>
|
||||
<th>Tier</th>
|
||||
<th>Market Range</th>
|
||||
<th>Suggested</th>
|
||||
<th>Age</th>
|
||||
<th>Final Price</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="pricing-tbody"><tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="loading" class="text-center py-5" style="display: none;">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
loadPricingData();
|
||||
|
||||
@@ -60,13 +146,10 @@ function renderPricingTable(serials) {
|
||||
serials.forEach(function(s) {
|
||||
var ageBadge = '<span class="badge badge-' + (s.age.color || 'secondary') + '">' +
|
||||
(s.age.status || 'unknown') + '</span>';
|
||||
|
||||
var tierBadge = '<span class="badge badge-info">' + (s.tier || 'N/A') + '</span>';
|
||||
|
||||
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 = '<tr data-age="' + s.age.status + '">' +
|
||||
@@ -81,11 +164,8 @@ function renderPricingTable(serials) {
|
||||
'<td>' + ageBadge + '<br><small>' + s.age.days + 'd</small></td>' +
|
||||
'<td><input type="number" class="form-control form-control-sm final-price" ' +
|
||||
'data-serial="' + s.serial_no + '" value="' + (s.assigned_price || '') + '" step="0.01"></td>' +
|
||||
'<td>' +
|
||||
'<button class="btn btn-sm btn-success btn-save-price" data-serial="' + s.serial_no + '">Save</button>' +
|
||||
'</td>' +
|
||||
'<td><button class="btn btn-sm btn-success btn-save-price" data-serial="' + s.serial_no + '">Save</button></td>' +
|
||||
'</tr>';
|
||||
|
||||
tbody.append(row);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
return context
|
||||
@@ -0,0 +1,83 @@
|
||||
<div class="container-fluid">
|
||||
<div class="page-header">
|
||||
<h2>Sales Manager — Pricing Queue</h2>
|
||||
<p class="text-muted">Review suggested prices, adjust tiers, and set final prices</p>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-success">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Current</h5>
|
||||
<p class="card-text">0-30 days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-dark bg-warning">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Stale</h5>
|
||||
<p class="card-text">30-60 days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white" style="background-color: #fd7e14;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Aging</h5>
|
||||
<p class="card-text">60-90 days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-danger">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Expired</h5>
|
||||
<p class="card-text">90+ days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-outline-primary" id="btn-all">All</button>
|
||||
<button type="button" class="btn btn-outline-success" id="btn-current">Current</button>
|
||||
<button type="button" class="btn btn-outline-warning" id="btn-stale">Stale</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="btn-aging">Aging</button>
|
||||
<button type="button" class="btn btn-outline-danger" id="btn-expired">Expired</button>
|
||||
</div>
|
||||
<button class="btn btn-primary float-right" id="btn-refresh">Refresh</button>
|
||||
<button class="btn btn-secondary float-right mr-2" id="btn-batch-score">Batch Score</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover" id="pricing-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Serial</th>
|
||||
<th>Item</th>
|
||||
<th>Grade</th>
|
||||
<th>Specs</th>
|
||||
<th>Score</th>
|
||||
<th>Tier</th>
|
||||
<th>Market Range</th>
|
||||
<th>Suggested</th>
|
||||
<th>Age</th>
|
||||
<th>Final Price</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="pricing-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="loading" class="text-center py-5" style="display: none;">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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(`
|
||||
<div class="container-fluid">
|
||||
<div class="page-header">
|
||||
<h2>Sales Manager — Pricing Queue</h2>
|
||||
<p class="text-muted">Review suggested prices, adjust tiers, and set final prices</p>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-success">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Current</h5>
|
||||
<p class="card-text">0-30 days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-dark bg-warning">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Stale</h5>
|
||||
<p class="card-text">30-60 days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white" style="background-color: #fd7e14;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Aging</h5>
|
||||
<p class="card-text">60-90 days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-danger">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Expired</h5>
|
||||
<p class="card-text">90+ days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-outline-primary" id="btn-all">All</button>
|
||||
<button type="button" class="btn btn-outline-success" id="btn-current">Current</button>
|
||||
<button type="button" class="btn btn-outline-warning" id="btn-stale">Stale</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="btn-aging">Aging</button>
|
||||
<button type="button" class="btn btn-outline-danger" id="btn-expired">Expired</button>
|
||||
</div>
|
||||
<button class="btn btn-primary float-right" id="btn-refresh">Refresh</button>
|
||||
<button class="btn btn-secondary float-right mr-2" id="btn-batch-score">Batch Score</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover" id="pricing-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Serial</th>
|
||||
<th>Item</th>
|
||||
<th>Grade</th>
|
||||
<th>Specs</th>
|
||||
<th>Score</th>
|
||||
<th>Tier</th>
|
||||
<th>Market Range</th>
|
||||
<th>Suggested</th>
|
||||
<th>Age</th>
|
||||
<th>Final Price</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="pricing-tbody"><tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="loading" class="text-center py-5" style="display: none;">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
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 = '<span class="badge badge-' + (s.age.color || 'secondary') + '">' +
|
||||
(s.age.status || 'unknown') + '</span>';
|
||||
var tierBadge = '<span class="badge badge-info">' + (s.tier || 'N/A') + '</span>';
|
||||
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 = '<tr data-age="' + s.age.status + '">' +
|
||||
'<td><a href="/app/serial-no/' + s.serial_no + '">' + s.serial_no + '</a></td>' +
|
||||
'<td>' + (s.item_name || s.item_code) + '</td>' +
|
||||
'<td>' + (s.cosmetic_grade || '') + '</td>' +
|
||||
'<td><small>' + specs.join(' / ') + '</small></td>' +
|
||||
'<td><strong>' + (s.score || 0) + '</strong></td>' +
|
||||
'<td>' + tierBadge + '</td>' +
|
||||
'<td><small>' + marketRange + '</small></td>' +
|
||||
'<td>$' + (s.suggested_price || 0).toFixed(2) + '</td>' +
|
||||
'<td>' + ageBadge + '<br><small>' + s.age.days + 'd</small></td>' +
|
||||
'<td><input type="number" class="form-control form-control-sm final-price" ' +
|
||||
'data-serial="' + s.serial_no + '" value="' + (s.assigned_price || '') + '" step="0.01"></td>' +
|
||||
'<td><button class="btn btn-sm btn-success btn-save-price" data-serial="' + s.serial_no + '">Save</button></td>' +
|
||||
'</tr>';
|
||||
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'});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user