Compare commits
3 Commits
05f84c134e
...
2e267b7c96
Author | SHA1 | Date | |
---|---|---|---|
2e267b7c96 | |||
6e99d9dd88 | |||
9f43a1c04c |
12
.gitignore
vendored
12
.gitignore
vendored
@ -1,12 +1,16 @@
|
|||||||
# ---> Rust
|
# ---> Rust
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
backend/debug/
|
frontend/debug/
|
||||||
backend/target/
|
frontend/target/
|
||||||
backend/.env
|
frontend/.env
|
||||||
|
frontend/dist
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
backend/*.pdb
|
frontend/*.pdb
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
.vscode/*
|
@ -1,12 +0,0 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct CarStation {
|
|
||||||
pub id: i32,
|
|
||||||
pub address: String
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct BindingCarStation {
|
|
||||||
pub address: String
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct Client {
|
|
||||||
pub id: i32,
|
|
||||||
pub name: String,
|
|
||||||
pub surname: String,
|
|
||||||
pub middlename: Option<String>,
|
|
||||||
pub phone: String
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct BindingClient {
|
|
||||||
pub name: String,
|
|
||||||
pub surname: String,
|
|
||||||
pub middlename: Option<String>,
|
|
||||||
pub phone: String
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
pub mod traits;
|
|
||||||
pub mod postgres;
|
|
423
frontend/app.py
Normal file
423
frontend/app.py
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
from flask import Flask, request, render_template, session, redirect, url_for
|
||||||
|
import requests
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.secret_key = 'Kill me already'
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
if request.method == 'POST':
|
||||||
|
if not request.form.get('username') or not request.form.get('password'):
|
||||||
|
return render_template('login.html', title='Авторизация', errors='Необходимо заполнить все поля',
|
||||||
|
logged_in=False)
|
||||||
|
|
||||||
|
response = requests.post("http://localhost:8080/api/login",
|
||||||
|
json={
|
||||||
|
'username': request.form.get('username'),
|
||||||
|
'password': request.form.get('password')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template('login.html', title='Авторизация', errors='Неверный логин или пароль',
|
||||||
|
logged_in=False)
|
||||||
|
|
||||||
|
session['token'] = response.json()['token']
|
||||||
|
session['administrator_id'] = response.json()['administrator_id']
|
||||||
|
session['car_station_id'] = response.json()['car_station_id']
|
||||||
|
|
||||||
|
return redirect(url_for('rents'))
|
||||||
|
|
||||||
|
return render_template('login.html', title='Авторизация', logged_in=False)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/logout')
|
||||||
|
def logout():
|
||||||
|
session.pop('token')
|
||||||
|
session.pop('administrator_id')
|
||||||
|
session.pop('car_station_id')
|
||||||
|
|
||||||
|
requests.post('http://localhost:8080/api/logout')
|
||||||
|
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/rents', methods=['GET', 'POST'])
|
||||||
|
def rents():
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
rents_data = requests.get('http://localhost:8080/api/rents/', headers={'Authorization': session['token']}).json()
|
||||||
|
cars = list(
|
||||||
|
filter(lambda c: c['car_station_id'] == session['car_station_id'],
|
||||||
|
requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
clients = requests.get('http://localhost:8080/api/clients/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
for rent in rents_data:
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
date_format = '%Y-%m-%dT%H:%M:%S.%f'
|
||||||
|
|
||||||
|
parsed_date = datetime.strptime(rent['start_time'], date_format)
|
||||||
|
rent['start_time'] = parsed_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
for car in cars:
|
||||||
|
if car['id'] == rent['car_id']:
|
||||||
|
rent['car'] = car
|
||||||
|
if not rent['time_amount']:
|
||||||
|
cars.remove(car)
|
||||||
|
break
|
||||||
|
for client in clients:
|
||||||
|
if client['id'] == rent['client_id']:
|
||||||
|
rent['client'] = client
|
||||||
|
break
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if not request.form.get('id') and (not request.form.get('client_id') or not request.form.get('car_id')):
|
||||||
|
return render_template(
|
||||||
|
'rents.html',
|
||||||
|
errors='Необходимо заполнить все поля',
|
||||||
|
title='Аренды',
|
||||||
|
rents=rents_data,
|
||||||
|
cars=cars,
|
||||||
|
clients=clients,
|
||||||
|
logged_in=True,
|
||||||
|
selected_client=int(request.args.get('client')) if request.args.get('client') else None,
|
||||||
|
selected_car=int(request.args.get('car')) if request.args.get('car') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
response = None
|
||||||
|
if request.form.get('id'):
|
||||||
|
response = requests.get(f'http://localhost:8080/api/rents/{request.form.get("id")}/end',
|
||||||
|
headers={'Authorization': session['token']})
|
||||||
|
else:
|
||||||
|
response = requests.post('http://localhost:8080/api/rents/', headers={'Authorization': session['token']}, json={
|
||||||
|
"client_id": int(request.form.get('client_id')),
|
||||||
|
"car_id": int(request.form.get('car_id'))
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template(
|
||||||
|
'rents.html',
|
||||||
|
title='Аренды',
|
||||||
|
errors=response.json(),
|
||||||
|
rents=rents_data,
|
||||||
|
cars=cars,
|
||||||
|
clients=clients,
|
||||||
|
logged_in=True,
|
||||||
|
selected_client=int(request.args.get('client')) if request.args.get('client') else None,
|
||||||
|
selected_car=int(request.args.get('car')) if request.args.get('car') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
if request.form.get('id'):
|
||||||
|
return redirect(url_for('rent', id=request.form.get('id')))
|
||||||
|
|
||||||
|
rents_data = requests.get('http://localhost:8080/api/rents/',
|
||||||
|
headers={'Authorization': session['token']}).json()
|
||||||
|
cars = list(
|
||||||
|
filter(lambda c: c['car_station_id'] == session['car_station_id'],
|
||||||
|
requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
clients = requests.get('http://localhost:8080/api/clients/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
for rent in rents_data:
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
date_format = '%Y-%m-%dT%H:%M:%S.%f'
|
||||||
|
|
||||||
|
parsed_date = datetime.strptime(rent['start_time'], date_format)
|
||||||
|
rent['start_time'] = parsed_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
for car in cars:
|
||||||
|
if car['id'] == rent['car_id']:
|
||||||
|
rent['car'] = car
|
||||||
|
if not rent['time_amount']:
|
||||||
|
cars.remove(car)
|
||||||
|
break
|
||||||
|
for client in clients:
|
||||||
|
if client['id'] == rent['client_id']:
|
||||||
|
rent['client'] = client
|
||||||
|
break
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'rents.html',
|
||||||
|
title='Аренды',
|
||||||
|
rents=rents_data,
|
||||||
|
cars=cars,
|
||||||
|
clients=clients,
|
||||||
|
logged_in=True,
|
||||||
|
selected_client=int(request.args.get('client')) if request.args.get('client') else None,
|
||||||
|
selected_car=int(request.args.get('car')) if request.args.get('car') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/rents/<int:id>', methods=['GET', 'POST'])
|
||||||
|
def rent(id):
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
rent = requests.get(f'http://localhost:8080/api/rents/{id}', headers={'Authorization': session['token']}).json()
|
||||||
|
client = requests.get(f'http://localhost:8080/api/clients/{rent["client_id"]}', headers={'Authorization': session['token']}).json()
|
||||||
|
car = requests.get(f'http://localhost:8080/api/cars/{rent["car_id"]}', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
rent['client'] = client
|
||||||
|
rent['car'] = car
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
date_format = '%Y-%m-%dT%H:%M:%S.%f'
|
||||||
|
|
||||||
|
parsed_date = datetime.strptime(rent['start_time'], date_format)
|
||||||
|
rent['start_time'] = parsed_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
import math
|
||||||
|
return render_template('rent.html', title='Просмотр аренды', rent=rent, logged_in=True, float=float, int=int, math=math)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/clients', defaults={'id': 0}, methods=['GET', 'POST'])
|
||||||
|
@app.route('/clients/<int:id>', methods=['GET', 'POST'])
|
||||||
|
def clients(id):
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
clients = requests.get('http://localhost:8080/api/clients/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if id != 0:
|
||||||
|
if not request.form.get('name') or not request.form.get('surname') or not request.form.get('middlename') or not request.form.get('phone'):
|
||||||
|
return render_template(
|
||||||
|
'clients.html',
|
||||||
|
title='Изменение клиента',
|
||||||
|
errors='Должны быть заполнены все поля!',
|
||||||
|
current_client=list(filter(lambda c: c['id'] == id, clients))[0],
|
||||||
|
clients=clients,
|
||||||
|
logged_in=True
|
||||||
|
)
|
||||||
|
|
||||||
|
response = requests.patch(f'http://localhost:8080/api/clients/{id}', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'name': request.form.get('name'),
|
||||||
|
'surname': request.form.get('surname'),
|
||||||
|
'middlename': request.form.get('middlename'),
|
||||||
|
'phone': request.form.get('phone')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template(
|
||||||
|
'clients.html',
|
||||||
|
title='Изменение клиента',
|
||||||
|
errors=response.json(),
|
||||||
|
clients=clients,
|
||||||
|
current_client=list(filter(lambda c: c['id'] == id, clients))[0],
|
||||||
|
logged_in=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('clients'))
|
||||||
|
else:
|
||||||
|
if not request.form.get('name') or not request.form.get('surname') or not request.form.get('middlename') or not request.form.get('phone'):
|
||||||
|
return render_template('clients.html', title='Клиенты', errors='Должны быть заполнены все поля!',
|
||||||
|
clients=clients, logged_in=True)
|
||||||
|
|
||||||
|
response = requests.post('http://localhost:8080/api/clients/', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'name': request.form.get('name'),
|
||||||
|
'surname': request.form.get('surname'),
|
||||||
|
'middlename': request.form.get('middlename'),
|
||||||
|
'phone': request.form.get('phone')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template('clients.html', title='Клиенты', errors=response.json(), clients=clients,
|
||||||
|
logged_in=True)
|
||||||
|
|
||||||
|
clients = requests.get('http://localhost:8080/api/clients/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
if id != 0:
|
||||||
|
return render_template('clients.html', title='Изменение клиента', clients=clients, logged_in=True, current_client=list(filter(lambda c: c['id'] == id, clients))[0])
|
||||||
|
|
||||||
|
return render_template('clients.html', title='Клиенты', clients=clients, logged_in=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/owners', defaults={'id': 0}, methods=['GET', 'POST'])
|
||||||
|
@app.route('/owners/<int:id>', methods=['GET', 'POST'])
|
||||||
|
def owners(id):
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
owners = requests.get('http://localhost:8080/api/owners/', headers={'Authorization': session['token']}).json()
|
||||||
|
cars = list(
|
||||||
|
filter(lambda c: c['car_station_id'] == session['car_station_id'],
|
||||||
|
requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if id != 0:
|
||||||
|
if not request.form.get('name') or not request.form.get('surname') or not request.form.get('middlename') or not request.form.get('phone'):
|
||||||
|
return render_template(
|
||||||
|
'owners.html',
|
||||||
|
title='Изменение владельца',
|
||||||
|
errors='Должны быть заполнены все поля!',
|
||||||
|
current_owner=list(filter(lambda c: c['id'] == id, owners))[0],
|
||||||
|
owners=owners,
|
||||||
|
cars=cars,
|
||||||
|
logged_in=True
|
||||||
|
)
|
||||||
|
|
||||||
|
response = requests.patch(f'http://localhost:8080/api/owners/{id}', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'name': request.form.get('name'),
|
||||||
|
'surname': request.form.get('surname'),
|
||||||
|
'middlename': request.form.get('middlename'),
|
||||||
|
'phone': request.form.get('phone')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template(
|
||||||
|
'owners.html',
|
||||||
|
title='Изменение владельца',
|
||||||
|
errors=response.json(),
|
||||||
|
owners=owners,
|
||||||
|
cars=cars,
|
||||||
|
current_owner=list(filter(lambda c: c['id'] == id, owners))[0],
|
||||||
|
logged_in=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('owners'))
|
||||||
|
else:
|
||||||
|
if not request.form.get('name') or not request.form.get('surname') or not request.form.get('middlename') or not request.form.get('phone'):
|
||||||
|
return render_template('owners.html', title='Владельцы', errors='Должны быть заполнены все поля!',
|
||||||
|
cars=cars,
|
||||||
|
owners=owners, logged_in=True)
|
||||||
|
|
||||||
|
response = requests.post('http://localhost:8080/api/owners/', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'name': request.form.get('name'),
|
||||||
|
'surname': request.form.get('surname'),
|
||||||
|
'middlename': request.form.get('middlename'),
|
||||||
|
'phone': request.form.get('phone')
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template('owners.html', title='Владельцы', errors=response.json(), owners=owners,
|
||||||
|
cars=cars,
|
||||||
|
logged_in=True)
|
||||||
|
|
||||||
|
owners = requests.get('http://localhost:8080/api/owners/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
if id != 0:
|
||||||
|
return render_template('owners.html', title='Изменение владельца', owners=owners, cars=cars, logged_in=True, current_owner=list(filter(lambda c: c['id'] == id, owners))[0])
|
||||||
|
|
||||||
|
return render_template('owners.html', title='Владельца', owners=owners, cars=cars, logged_in=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/cars', defaults={'id': 0}, methods=['GET', 'POST'])
|
||||||
|
@app.route('/cars/<int:id>', methods=['GET', 'POST'])
|
||||||
|
def cars(id):
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
owners = requests.get('http://localhost:8080/api/owners/', headers={'Authorization': session['token']}).json()
|
||||||
|
cars = list(
|
||||||
|
filter(lambda c: c['car_station_id'] == session['car_station_id'],
|
||||||
|
requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if id != 0:
|
||||||
|
if not request.form.get('brand') or not request.form.get('model') or not request.form.get('price') or not request.form.get('owner_id'):
|
||||||
|
return render_template(
|
||||||
|
'cars.html',
|
||||||
|
title='Изменение автомобиля',
|
||||||
|
errors='Должны быть заполнены все поля!',
|
||||||
|
current_car=list(filter(lambda c: c['id'] == id, cars))[0],
|
||||||
|
cars=cars,
|
||||||
|
owners=owners,
|
||||||
|
logged_in=True,
|
||||||
|
selected_owner=int(request.args.get('owner')) if request.args.get('owner') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
response = requests.patch(f'http://localhost:8080/api/cars/{id}', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'brand': request.form.get('brand'),
|
||||||
|
'model': request.form.get('model'),
|
||||||
|
'price': float(request.form.get('price')),
|
||||||
|
'owner_id': int(request.form.get('owner_id')),
|
||||||
|
'car_station_id': session['car_station_id']
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template(
|
||||||
|
'cars.html',
|
||||||
|
title='Изменение автомобиля',
|
||||||
|
errors=response.json(),
|
||||||
|
cars=cars,
|
||||||
|
owners=owners,
|
||||||
|
current_car=list(filter(lambda c: c['id'] == id, cars))[0],
|
||||||
|
logged_in=True,
|
||||||
|
selected_owner=int(request.args.get('owner')) if request.args.get('owner') else None
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('cars'))
|
||||||
|
else:
|
||||||
|
if not request.form.get('brand') or not request.form.get('model') or not request.form.get('price') or not request.form.get('owner_id'):
|
||||||
|
return render_template('cars.html', title='Автомобили', errors='Должны быть заполнены все поля!',
|
||||||
|
owners=owners, selected_owner=int(request.args.get('owner')) if request.args.get('owner') else None,
|
||||||
|
cars=cars, logged_in=True)
|
||||||
|
|
||||||
|
response = requests.post('http://localhost:8080/api/cars/', headers={'Authorization': session['token']},
|
||||||
|
json={
|
||||||
|
'brand': request.form.get('brand'),
|
||||||
|
'model': request.form.get('model'),
|
||||||
|
'price': float(request.form.get('price')),
|
||||||
|
'owner_id': int(request.form.get('owner_id')),
|
||||||
|
'car_station_id': session['car_station_id']
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return render_template('cars.html', title='Автомобили', errors=response.json(), cars=cars,
|
||||||
|
owners=owners, selected_owner=int(request.args.get('owner')) if request.args.get('owner') else None,
|
||||||
|
logged_in=True)
|
||||||
|
|
||||||
|
cars = requests.get('http://localhost:8080/api/cars/', headers={'Authorization': session['token']}).json()
|
||||||
|
|
||||||
|
if id != 0:
|
||||||
|
return render_template('cars.html', title='Изменение автомобиля', cars=cars, logged_in=True, owners=owners, selected_owner=int(request.args.get('owner')) if request.args.get('owner') else None, current_car=list(filter(lambda c: c['id'] == id, cars))[0])
|
||||||
|
|
||||||
|
return render_template('cars.html', title='Автомобили', cars=cars, logged_in=True, owners=owners, selected_owner=int(request.args.get('owner')) if request.args.get('owner') else None)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/report')
|
||||||
|
def report():
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
response = requests.get('http://localhost:8080/api/cars/report', headers={'Authorization': session['token']})
|
||||||
|
reports = list()
|
||||||
|
for report in response.json():
|
||||||
|
report['income'] = float(report['income'])
|
||||||
|
reports.append(report)
|
||||||
|
|
||||||
|
return render_template('report.html', title='Отчёт', reports=reports, logged_in=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/admin', methods=['GET', 'POST'])
|
||||||
|
def admin():
|
||||||
|
if not session['token']:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
response = requests.get('http://localhost:8080/benchmark', headers={'Authorization': session['token']})
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
return render_template('admin.html', title='Панель администратора', logged_in=True, result=result)
|
||||||
|
|
||||||
|
return render_template('admin.html', title='Панель администратора', logged_in=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run()
|
BIN
frontend/requirements.txt
Normal file
BIN
frontend/requirements.txt
Normal file
Binary file not shown.
18
frontend/templates/admin.html
Normal file
18
frontend/templates/admin.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col card is-vertical-align" method="post">
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">Произвести прирост населения машин и клиентов</button>
|
||||||
|
</div>
|
||||||
|
{% if result %}
|
||||||
|
<div class="alert alert-primary mb-3 h4" role="alert">
|
||||||
|
Это заняло вот столько микросекунд: {{ result }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
36
frontend/templates/base.html
Normal file
36
frontend/templates/base.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/chota@latest">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.0.slim.min.js" integrity="sha256-tG5mcZUtJsZvyKAxYLVXrmjKBVLd6VpVccqz/r4ypFE=" crossorigin="anonymous"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container mt-5">
|
||||||
|
{% if logged_in %}
|
||||||
|
<div class="nav mb-5">
|
||||||
|
<div class="nav-left">
|
||||||
|
<a href="/rents" class="button primary outline">Аренды</a>
|
||||||
|
<a href="/clients" class="button primary outline">Клиенты</a>
|
||||||
|
<a href="/owners" class="button primary outline">Владельцы</a>
|
||||||
|
<a href="/cars" class="button primary outline">Автомобили</a>
|
||||||
|
<a href="/report" class="button primary outline">Отчёт</a>
|
||||||
|
</div>
|
||||||
|
<div class="nav-right">
|
||||||
|
<a href="/logout" class="button danger outline">Выйти</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% block script %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
74
frontend/templates/cars.html
Normal file
74
frontend/templates/cars.html
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col-6 card is-vertical-align" method="post">
|
||||||
|
<h3 class="is-center mb-5">{% if current_car %} Просмотр автомобиля {% else %} Регистрация автомобиля {% endif %}</h3>
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Марка: <input type="text" name="brand" {% if current_car %} value="{{ current_car.brand }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Модель: <input type="text" name="model" {% if current_car %} value="{{ current_car.model }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Цена: <input type="text" name="price" pattern="\d+\.\d{2}" {% if current_car %} value="{{ current_car.price }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">
|
||||||
|
Владелец:
|
||||||
|
<select id="owner_id" name="owner_id" class="mt-2">
|
||||||
|
{% for owner in owners %}
|
||||||
|
<option {% if selected_owner and selected_owner == owner['id'] %} selected {% endif %} value="{{ owner['id'] }}">ФИО: {{ owner['surname'] }} {{ owner['name'][0] }}. {{ owner['middlename'][0] }}. Телефон: {{ owner['phone'] }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">{% if current_car %} Изменить {% else %} Зарегистрировать автомобиль {% endif %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Все автомобили на стоянке</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Марка</th>
|
||||||
|
<th class="h4">Модель</th>
|
||||||
|
<th class="h4">Цена</th>
|
||||||
|
<th class="h4">Владелец</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for car in cars|sort(attribute="brand")|sort(attribute="model")|sort(attribute="price") %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ car.brand }}</td>
|
||||||
|
<td class="h4">{{ car.model }}</td>
|
||||||
|
<td class="h4">{{ car.price }}</td>
|
||||||
|
<td class="h4"><a href="{{ url_for('owners', id=car.owner_id) }}">Перейти к владельцу</a></td>
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<a href="{{ url_for("rents", car=car.id) }}" class="button primary outline col">Арендовать</a>
|
||||||
|
<a href="{{ url_for("cars", id=car.id) }}" class="button primary outline col">Посмотреть</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#owner_id').select2();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
59
frontend/templates/clients.html
Normal file
59
frontend/templates/clients.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col card is-vertical-align" method="post">
|
||||||
|
<h3 class="is-center mb-5">{% if current_client %} Просмотр клиента {% else %} Регистрация клиента {% endif %}</h3>
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Имя: <input type="text" name="name" {% if current_client %} value="{{ current_client.name }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Фамилия: <input type="text" name="surname" {% if current_client %} value="{{ current_client.surname }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Отчество: <input type="text" name="middlename" {% if current_client %} value="{{ current_client.middlename }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Номер телефона: <input type="text" name="phone" pattern="8\d{10}" {% if current_client %} value="{{ current_client.phone }}" {% else %} value="8" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">{% if current_client %} Изменить {% else %} Зарегистрировать клиента {% endif %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Все клиенты</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Номер телефона</th>
|
||||||
|
<th class="h4">Фамилия</th>
|
||||||
|
<th class="h4">Имя</th>
|
||||||
|
<th class="h4">Отчество</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for client in clients|sort(attribute="surname")|sort(attribute="name")|sort(attribute="middlename") %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ client.phone }}</td>
|
||||||
|
<td class="h4">{{ client.surname }}</td>
|
||||||
|
<td class="h4">{{ client.name }}</td>
|
||||||
|
<td class="h4">{{ client.middlename }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<a href="{{ url_for("rents", client=client.id) }}" class="button primary outline col">Перейти к аренде</a>
|
||||||
|
<a href="{{ url_for("clients", id=client.id) }}" class="button primary outline col">Посмотреть</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
24
frontend/templates/login.html
Normal file
24
frontend/templates/login.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col card is-vertical-align" method="post">
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Логин: <input type="text" name="username"/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Пароль: <input type="password" name="password"/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">Войти</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
91
frontend/templates/owners.html
Normal file
91
frontend/templates/owners.html
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col card is-vertical-align" method="post">
|
||||||
|
<h3 class="is-center mb-5">{% if current_owner %} Просмотр владельца {% else %} Регистрация владельца {% endif %}</h3>
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Имя: <input type="text" name="name" {% if current_owner %} value="{{ current_owner.name }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Фамилия: <input type="text" name="surname" {% if current_owner %} value="{{ current_owner.surname }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Отчество: <input type="text" name="middlename" {% if current_owner %} value="{{ current_owner.middlename }}" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">Номер телефона: <input type="text" name="phone" pattern="8\d{10}" {% if current_owner %} value="{{ current_owner.phone }}" {% else %} value="8" {% endif %}/></label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">{% if current_owner %} Изменить {% else %} Зарегистрировать клиента {% endif %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
{% if current_owner %}
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Автомобили владельца</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Марка</th>
|
||||||
|
<th class="h4">Модель</th>
|
||||||
|
<th class="h4">Цена</th>
|
||||||
|
<th class="h4">Владелец</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for car in cars|sort(attribute="brand")|sort(attribute="model")|sort(attribute="price") %}
|
||||||
|
{% if car.owner_id == current_owner.id %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ car.brand }}</td>
|
||||||
|
<td class="h4">{{ car.model }}</td>
|
||||||
|
<td class="h4">{{ car.price }}</td>
|
||||||
|
<td class="h4"><a href="{{ url_for('owners', id=car.owner_id) }}">Перейти к владельцу</a></td>
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<a href="{{ url_for("rents", car=car.id) }}" class="button primary outline col">Арендовать</a>
|
||||||
|
<a href="{{ url_for("cars", id=car.id) }}" class="button primary outline col">Посмотреть</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Все владельцы</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Номер телефона</th>
|
||||||
|
<th class="h4">Фамилия</th>
|
||||||
|
<th class="h4">Имя</th>
|
||||||
|
<th class="h4">Отчество</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for owner in owners|sort(attribute="surname")|sort(attribute="name")|sort(attribute="middlename") %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ owner.phone }}</td>
|
||||||
|
<td class="h4">{{ owner.surname }}</td>
|
||||||
|
<td class="h4">{{ owner.name }}</td>
|
||||||
|
<td class="h4">{{ owner.middlename }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<a href="{{ url_for("cars", owner=owner.id) }}" class="button primary outline col">Добавить автомобиль</a>
|
||||||
|
<a href="{{ url_for("owners", id=owner.id) }}" class="button primary outline col">Посмотреть</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
34
frontend/templates/rent.html
Normal file
34
frontend/templates/rent.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-6 card h4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
Дата начала: {{ rent.start_time }}
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
Номер телефона клиента: {{ rent.client.phone }}
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
<span>Клиент: <a href="{{ url_for('clients', id=rent.client_id) }}">{{ rent.client.surname }} {{ rent.client.name }} {{ rent.client.middlename }}</a></span>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
<span>Автомобиль: <a href="{{ url_for('cars', id=rent.car_id) }}">{{ rent.car.brand }} {{ rent.car.model }}</a></span>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
Количество минут: {{ rent.time_amount }}
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 is-left">
|
||||||
|
<span>Итоговая стоимость: <strong>{{ math.ceil(float(rent.car.price) * int(rent.time_amount) * 100) / 100 }}</strong></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
109
frontend/templates/rents.html
Normal file
109
frontend/templates/rents.html
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align mb-5">
|
||||||
|
<div class="col"></div>
|
||||||
|
<form class="col-6 card is-vertical-align" method="post">
|
||||||
|
<h3 class="is-center mb-5">Создание аренды</h3>
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger mb-3 h4" role="alert">
|
||||||
|
{{ errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">
|
||||||
|
Клиент:
|
||||||
|
<select id="client_id" name="client_id" class="mt-2">
|
||||||
|
{% for client in clients %}
|
||||||
|
<option {% if selected_client and selected_client == client['id'] %} selected {% endif %} value="{{ client['id'] }}">ФИО: {{ client['surname'] }} {{ client['name'][0] }}. {{ client['middlename'][0] }}. Телефон: {{ client['phone'] }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="h4">
|
||||||
|
Машина:
|
||||||
|
<select id="car_id" name="car_id" class="mt-2">
|
||||||
|
{% for car in cars %}
|
||||||
|
<option {% if selected_car and selected_car == car['id'] %} selected {% endif %} value="{{ car['id'] }}">Марка: {{ car['brand'] }}. Модель: {{ car['model'] }}. Цена: {{ car['price'] }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<button class="button primary is-center">Создать запись об аренде</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Действительные аренды</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Дата начала</th>
|
||||||
|
<th class="h4">Номер телефона клиента</th>
|
||||||
|
<th class="h4">Клиент</th>
|
||||||
|
<th class="h4">Автомобиль</th>
|
||||||
|
<th class="h4">Цена за минуту</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for rent in rents|sort(reverse=true, attribute="start_time") %}
|
||||||
|
{% if not rent.time_amount %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ rent.start_time }}</td>
|
||||||
|
<td class="h4">{{ rent.client.phone }}</td>
|
||||||
|
<td class="h4"><a href="{{ url_for('clients', id=rent.client_id) }}">{{ rent.client.surname }} {{ rent.client.name }} {{ rent.client.middlename }}</a></td>
|
||||||
|
<td class="h4"><a href="{{ url_for('cars', id=rent.car_id) }}">{{ rent.car.brand }} {{ rent.car.model }}</a></td>
|
||||||
|
<td class="h4">{{ rent.car.price }}</td>
|
||||||
|
<td>
|
||||||
|
<form method="post" class="row">
|
||||||
|
<input name="id" value="{{ rent.id }}" style="visibility: hidden">
|
||||||
|
<button class="button primary outline col">Завершить</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Завершённые аренды</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Дата начала</th>
|
||||||
|
<th class="h4">Номер телефона клиента</th>
|
||||||
|
<th class="h4">Клиент</th>
|
||||||
|
<th class="h4">Автомобиль</th>
|
||||||
|
<th class="h4">Цена за минуту</th>
|
||||||
|
<th class="h4">Действия</th>
|
||||||
|
</tr>
|
||||||
|
{% for rent in rents|sort(reverse=true, attribute="start_time") %}
|
||||||
|
{% if rent.time_amount %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4">{{ rent.start_time }}</td>
|
||||||
|
<td class="h4">{{ rent.client.phone }}</td>
|
||||||
|
<td class="h4"><a href="{{ url_for('clients', id=rent.client_id) }}">{{ rent.client.surname }} {{ rent.client.name }} {{ rent.client.middlename }}</a></td>
|
||||||
|
<td class="h4"><a href="{{ url_for('cars', id=rent.car_id) }}">{{ rent.car.brand }} {{ rent.car.model }}</a></td>
|
||||||
|
<td class="h4">{{ rent.car.price }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('rent', id=rent.id) }}" class="button primary outline">Посмотреть</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#client_id').select2();
|
||||||
|
$('#car_id').select2();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
23
frontend/templates/report.html
Normal file
23
frontend/templates/report.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row is-vertical-align mt-5">
|
||||||
|
<div class="card col table table-striped">
|
||||||
|
<h2 class="is-center mb-4">Отчёт по машинам</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="h4">Автомобиль</th>
|
||||||
|
<th class="h4">Взят в аренду в этом месяце (раз)</th>
|
||||||
|
<th class="h4">Доход с машины за этот месяц</th>
|
||||||
|
</tr>
|
||||||
|
{% for report in reports|sort(reverse=true, attribute="income") %}
|
||||||
|
<tr>
|
||||||
|
<td class="h4"><a href="{{ url_for('cars', id=report.car_id) }}">{{ report.brand }} {{ report.model }}</a></td>
|
||||||
|
<td class="h4">{{ report.times }}</td>
|
||||||
|
<td class="h4"> {{ report.income }} </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user