fix(customers): allow cashiers to view customers and use refreshed token
This commit is contained in:
@@ -11,13 +11,13 @@ customers_bp = Blueprint('customers', __name__, url_prefix='/pos/api/customers')
|
||||
|
||||
|
||||
def _can_view_customers():
|
||||
"""Taller/counter employees need customer autocomplete even without customers.view."""
|
||||
return g.employee_role == 'owner' or 'customers.view' in g.permissions or g.employee_role in ('workshop', 'mechanic', 'counter')
|
||||
"""Cashiers, counter and workshop employees need customer access for POS/service flows."""
|
||||
return g.employee_role == 'owner' or 'customers.view' in g.permissions or g.employee_role in ('cashier', 'counter', 'workshop', 'mechanic')
|
||||
|
||||
|
||||
def _can_create_customer():
|
||||
"""Taller/counter employees can create customers on the fly from an order."""
|
||||
return g.employee_role == 'owner' or 'customers.create' in g.permissions or g.employee_role in ('workshop', 'mechanic', 'counter')
|
||||
"""Cashiers, counter and workshop employees can create customers on the fly."""
|
||||
return g.employee_role == 'owner' or 'customers.create' in g.permissions or g.employee_role in ('cashier', 'counter', 'workshop', 'mechanic')
|
||||
|
||||
|
||||
# ─── Customer CRUD ─────────────────────────────
|
||||
@@ -126,8 +126,10 @@ def list_customers():
|
||||
|
||||
|
||||
@customers_bp.route('/<int:customer_id>', methods=['GET'])
|
||||
@require_auth('customers.view')
|
||||
@require_auth()
|
||||
def get_customer(customer_id):
|
||||
if not _can_view_customers():
|
||||
return jsonify({'error': 'Missing permissions: customers.view'}), 403
|
||||
"""Get customer details with credit info, vehicle history, and recent purchases."""
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
@@ -198,8 +200,10 @@ def get_customer(customer_id):
|
||||
|
||||
|
||||
@customers_bp.route('/<int:customer_id>/purchases', methods=['GET'])
|
||||
@require_auth('customers.view')
|
||||
@require_auth()
|
||||
def get_customer_purchases(customer_id):
|
||||
if not _can_view_customers():
|
||||
return jsonify({'error': 'Missing permissions: customers.view'}), 403
|
||||
"""Return full purchase history for a customer."""
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
@@ -379,8 +383,10 @@ def delete_customer(customer_id):
|
||||
|
||||
|
||||
@customers_bp.route('/<int:customer_id>/statement', methods=['GET'])
|
||||
@require_auth('customers.view')
|
||||
@require_auth()
|
||||
def customer_statement(customer_id):
|
||||
if not _can_view_customers():
|
||||
return jsonify({'error': 'Missing permissions: customers.view'}), 403
|
||||
"""Account statement: sales (invoices), payments, running balance.
|
||||
|
||||
Query params:
|
||||
@@ -479,8 +485,10 @@ def customer_statement(customer_id):
|
||||
|
||||
|
||||
@customers_bp.route('/<int:customer_id>/vehicles', methods=['GET'])
|
||||
@require_auth('customers.view')
|
||||
@require_auth()
|
||||
def customer_vehicles(customer_id):
|
||||
if not _can_view_customers():
|
||||
return jsonify({'error': 'Missing permissions: customers.view'}), 403
|
||||
"""Get customer's vehicle list with last purchases per vehicle.
|
||||
|
||||
Vehicle info is stored as JSONB in customers.vehicle_info:
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* Wired to the design-system HTML (customers.html).
|
||||
*/
|
||||
const Customers = (() => {
|
||||
let token = localStorage.getItem('pos_token') || '';
|
||||
let currentPage = 1;
|
||||
let totalPages = 1;
|
||||
let currentCustomer = null;
|
||||
@@ -16,6 +15,10 @@ const Customers = (() => {
|
||||
const userPerms = user.permissions || [];
|
||||
const canDeleteCustomer = userRole === 'owner' || userRole === 'admin' || userPerms.includes('customers.delete');
|
||||
|
||||
function getToken() {
|
||||
return localStorage.getItem('pos_token') || '';
|
||||
}
|
||||
|
||||
const fmt = (n) => '$' + parseFloat(n || 0).toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2, maximumFractionDigits: 2
|
||||
});
|
||||
@@ -28,7 +31,7 @@ const Customers = (() => {
|
||||
};
|
||||
|
||||
function headers() {
|
||||
return { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token };
|
||||
return { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + getToken() };
|
||||
}
|
||||
|
||||
async function api(url, options = {}) {
|
||||
@@ -733,7 +736,7 @@ const Customers = (() => {
|
||||
// ─── Init ────────────────────────────
|
||||
function init() {
|
||||
// Auth check
|
||||
if (!token) {
|
||||
if (!getToken()) {
|
||||
window.location.href = '/pos/login';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -656,7 +656,7 @@
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=44" defer></script>
|
||||
<script src="/pos/static/js/virtual-scroll.js" defer></script>
|
||||
<script src="/pos/static/js/customers.js?v=34" defer></script>
|
||||
<script src="/pos/static/js/customers.js?v=35" defer></script>
|
||||
<script src="/pos/static/js/offline-banner.js" defer></script>
|
||||
<script src="/pos/static/js/sync-engine.js" defer></script>
|
||||
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
|
||||
|
||||
Reference in New Issue
Block a user