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 @@
-
+