diff --git a/pos/blueprints/service_order_bp.py b/pos/blueprints/service_order_bp.py index 14ff9bd..5ffb82d 100644 --- a/pos/blueprints/service_order_bp.py +++ b/pos/blueprints/service_order_bp.py @@ -374,6 +374,39 @@ def assign_mechanic_endpoint(so_id): conn.close() +@service_order_bp.route('/vehicles', methods=['POST']) +@require_auth('workshop.edit') +def create_vehicle_for_workshop(): + """Create a fleet vehicle directly from the workshop flow.""" + data = request.get_json() or {} + if not data.get('plate') and not data.get('vin'): + return jsonify({'error': 'Se requiere placa o VIN'}), 400 + + conn = get_tenant_conn(g.tenant_id) + cur = conn.cursor() + try: + cur.execute(""" + INSERT INTO fleet_vehicles + (branch_id, plate, vin, make, model, year, + current_mileage, fuel_type, color, owner_name, notes) + VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) + RETURNING id + """, ( + data.get('branch_id', g.branch_id), data.get('plate'), data.get('vin'), + data.get('make'), data.get('model'), data.get('year'), + data.get('current_mileage', 0), data.get('fuel_type', 'gasolina'), + data.get('color'), data.get('owner_name'), data.get('notes'), + )) + vehicle_id = cur.fetchone()[0] + conn.commit() + cur.close(); conn.close() + return jsonify({'id': vehicle_id, 'message': 'Vehiculo creado'}), 201 + except Exception as e: + conn.rollback() + cur.close(); conn.close() + return jsonify({'error': str(e)}), 500 + + @service_order_bp.route('/inventory-search', methods=['GET']) @require_auth('workshop.view') def inventory_search(): diff --git a/pos/static/js/workshop.js b/pos/static/js/workshop.js index 9ce9e10..f70075f 100644 --- a/pos/static/js/workshop.js +++ b/pos/static/js/workshop.js @@ -704,6 +704,51 @@ var Workshop = (function() { } } + function openNewVehicleModal() { + document.getElementById('newVehicleModal').classList.add('is-open'); + document.getElementById('nvPlate').value = ''; + document.getElementById('nvVIN').value = ''; + document.getElementById('nvMake').value = ''; + document.getElementById('nvModel').value = ''; + document.getElementById('nvYear').value = ''; + document.getElementById('nvColor').value = ''; + document.getElementById('nvOwner').value = ''; + } + + function closeNewVehicleModal() { + document.getElementById('newVehicleModal').classList.remove('is-open'); + } + + async function saveNewVehicle() { + var plate = document.getElementById('nvPlate').value.trim(); + var vin = document.getElementById('nvVIN').value.trim(); + if (!plate && !vin) { + alert('Se requiere al menos placa o VIN'); + return; + } + var payload = { + plate: plate, + vin: vin, + make: document.getElementById('nvMake').value.trim() || null, + model: document.getElementById('nvModel').value.trim() || null, + year: document.getElementById('nvYear').value ? parseInt(document.getElementById('nvYear').value, 10) : null, + color: document.getElementById('nvColor').value.trim() || null, + owner_name: document.getElementById('nvOwner').value.trim() || null, + branch_id: currentOrder ? currentOrder.branch_id : null + }; + try { + var res = await api('POST', '/vehicles', payload); + var label = plate + (payload.make ? ' · ' + payload.make : '') + (payload.model ? ' ' + payload.model : ''); + selectedEditVehicle = {id: res.id, label: label.trim()}; + document.getElementById('eoVehicleSearch').value = selectedEditVehicle.label; + document.getElementById('eoVehicleId').value = res.id; + toast('Vehículo creado'); + closeNewVehicleModal(); + } catch (e) { + alert('Error: ' + e.message); + } + } + // ─── Actions ─── function changeStatus() { @@ -1113,6 +1158,9 @@ var Workshop = (function() { selectEditVehicle: selectEditVehicle, searchCustomersForSO: searchCustomersForSO, searchVehiclesForSO: searchVehiclesForSO, + openNewVehicleModal: openNewVehicleModal, + closeNewVehicleModal: closeNewVehicleModal, + saveNewVehicle: saveNewVehicle, }; })(); diff --git a/pos/templates/workshop.html b/pos/templates/workshop.html index d430c0e..d5e2b5a 100644 --- a/pos/templates/workshop.html +++ b/pos/templates/workshop.html @@ -15,7 +15,7 @@ - +