## Step 1: Define the symbolic representation of the problem

Let's denote the number of bottles of French perfume as $x_1$ and the number of bottles of Spanish perfume as $x_2$. The objective is to minimize the total cost, which is $50x_1 + 45x_2$. The constraints are:

- The mixture must contain at least 150 units of flower scent: $20x_1 + 30x_2 \geq 150$
- The mixture must contain at least 60 units of fruit scent: $50x_1 + 40x_2 \geq 60$
- The number of bottles of each perfume cannot be negative: $x_1 \geq 0, x_2 \geq 0$

## 2: Convert the problem into a symbolic representation

The symbolic representation of the problem is:
```json
{
    'sym_variables': [('x1', 'French perfume'), ('x2', 'Spanish perfume')],
    'objective_function': '50*x1 + 45*x2',
    'constraints': [
        '20*x1 + 30*x2 >= 150',
        '50*x1 + 40*x2 >= 60',
        'x1 >= 0',
        'x2 >= 0'
    ]
}
```

## 3: Implement the problem in Gurobi code

```python
import gurobi

# Create a new model
model = gurobi.Model()

# Define the variables
x1 = model.addVar(name='French_perfume', lb=0, vtype=gurobi.GRB.CONTINUOUS)
x2 = model.addVar(name='Spanish_perfume', lb=0, vtype=gurobi.GRB.CONTINUOUS)

# Define the objective function
model.setObjective(50*x1 + 45*x2, gurobi.GRB.MINIMIZE)

# Define the constraints
model.addConstr(20*x1 + 30*x2 >= 150, name='flower_scent_constraint')
model.addConstr(50*x1 + 40*x2 >= 60, name='fruit_scent_constraint')

# Optimize the model
model.optimize()

# Print the solution
if model.status == gurobi.GRB.OPTIMAL:
    print('Optimal solution found.')
    print(f'Number of bottles of French perfume: {x1.varValue}')
    print(f'Number of bottles of Spanish perfume: {x2.varValue}')
    print(f'Total cost: {model.objVal}')
else:
    print('No optimal solution found.')
```