Skip to content

Commit

Permalink
UPD: Improved purchase verification
Browse files Browse the repository at this point in the history
- Favorites can only be visible items, that are not recharge items
  • Loading branch information
AlexanderKaschta committed Feb 29, 2024
1 parent f4a78bb commit 0b49709
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 30 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ SQLALCHEMY_DATABASE_URI='postgresql://nanpos:nanpos@localhost:5432/nanpos',
TERMINAL_LOGOUT_TIMEOUT=30, # logout timeout for Terminal mode in seconds, set to none to disable
QUICK_CANCEL_SEC=60, # Second-Limit for canceling a revenue
PURCHASE_COOLDOWN=0.0, # Cooldown between multiple purchases in seconds. If zero, there's no cooldown.
VERIFY_FREE_PURCHASES=False, # Option, whether purchases with no cost must be verified
VERIFY_FREE_PURCHASES_NOTE=None, # Optional text, that is displayed during confirmation
BANK_DATA=None, # Display bank data
FAVORITES_DISPLAY=3, # Amount of favorite products which should get highlighted
FAVORITES_DAYS=100, # Timespan for calculation of favorite products in Days
Expand Down
2 changes: 2 additions & 0 deletions nanposweb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def create_app(test_config=None):
TERMINAL_LOGOUT_TIMEOUT=30, # logout timeout for Terminal mode in seconds, set to none to disable
QUICK_CANCEL_SEC=60, # Second-Limit for canceling a revenue
PURCHASE_COOLDOWN=0.0, # Cooldown between multiple purchases in seconds. If zero, there's no cooldown.
VERIFY_FREE_PURCHASES=False, # Option, whether purchases with no cost must be verified
VERIFY_FREE_PURCHASES_NOTE=None, # Optional text, that is displayed during confirmation
BANK_DATA=None,
FAVORITES_DISPLAY=3, # Amount of favorite products which should get highlighted
FAVORITES_DAYS=100, # Timespan for calculation of favorite products in Days
Expand Down
37 changes: 30 additions & 7 deletions nanposweb/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ def index():
last_revenue = False

most_buyed_timestamp = datetime.datetime.now() - datetime.timedelta(days=current_app.config['FAVORITES_DAYS'])
most_buyed_query = db.select(Product, db.func.count(Revenue.product).label('CTR')).join(Product).where(
Revenue.product is not None).where(Revenue.user == user_id).where(
Revenue.date >= most_buyed_timestamp).group_by(Product.id).order_by(db.desc('CTR'))
most_buyed_query = (db.select(Product, db.func.count(Revenue.product).label('CTR')).join(Product)
.where(Revenue.product is not None).where(Revenue.user == user_id)
.where(Revenue.date >= most_buyed_timestamp).where(Product.visible)
.where(Product.price >= 0).group_by(Product.id).order_by(db.desc('CTR')))
most_buyed = db.session.execute(most_buyed_query).all()
favorites = [f[0] for f in most_buyed[:current_app.config.get('FAVORITES_DISPLAY')]]

Expand Down Expand Up @@ -116,10 +117,7 @@ def index_post():
if current_app.config.get("SHOW_BALANCE_AND_PRICE", True):
flash(f'Bought {product.name} for {format_currency(product.price)}{user_message}', category='success')
else:
if product.price < 0.0:
flash("Please ensure that you have paid for the item on the other device!", category='warning')
else:
flash(f'Bought {product.name}', category='success')
flash(f'Bought {product.name}', category='success')
if session.get('terminal', False):
return redirect(url_for('auth.logout'))
else:
Expand All @@ -129,6 +127,31 @@ def index_post():
return redirect(url_for('main.index'))


@main_bp.route("/verify-purchase", methods=['GET'])
@login_required
def verify_purchase():
if not current_app.config.get("VERIFY_FREE_PURCHASES", False):
# Redirect to the verification page
return redirect(url_for("main.index"))
if request.args.get('product') is None:
# Redirect to the verification page
return redirect(url_for("main.index"))

user_id = get_user_id()
balance = get_balance(user_id)
form = MainForm()

product_id = request.args.get('product')
if product_id is None:
return redirect(url_for('main.index'))

product = Product.query.filter_by(id=product_id).first()
if product is None:
return redirect(url_for('main.index'))

return render_template('verify.html', balance=balance, form=form, product=product)


@main_bp.route('/quick-cancel', methods=['GET'])
@login_required
def quick_cancel():
Expand Down
91 changes: 69 additions & 22 deletions nanposweb/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
{% endif %}

<div class="row-cols-auto d-flex ms-auto">
<div>
<div class="mx-1">
<span class="nav-link text-dark">User: {{ user_name }}</span>
</div>
{% if config.SHOW_BALANCE_AND_PRICE %}
Expand Down Expand Up @@ -53,33 +53,80 @@
{% for product in products %}
{% if product.visible or view_all %}
<div class="d-grid">
{% if product in favorites %}
<button type="submit" class="btn btn-primary py-5 ps-5" name="product_id"
{% elif product.price < 0 %}
<button type="submit" class="btn btn-outline-dark py-5 ps-5" name="product_id"
{% if product.price < 0 %}
{% if config.VERIFY_FREE_PURCHASES %}
<a class="btn btn-outline-dark py-5 ps-5" href="{{ url_for('main.verify_purchase', product=product.id) }}">
{% if product.is_food %}
{% set icon = (food_icons | random) %}
{% else %}
{% if product.has_alc %}
{% set icon = (alc_icons | random) %}
{% else %}
{% set icon = (drink_icons | random) %}
{% endif %}
{% endif %}
<i class="fa-solid fa-{{ icon }} fa-3x" style="float: left"></i>
{{ product.name }}<br>
{% if config.SHOW_BALANCE_AND_PRICE %}
{{ macro.render_currency(product.price) }}
{% else %}
{{ macro.render_contingent(balance, product.price) }}
{% endif %}
</a>
{% else %}
<button type="submit" class="btn btn-outline-dark py-5 ps-5" name="product_id" value="{{ product.id }}">
{% if product.is_food %}
{% set icon = (food_icons | random) %}
{% else %}
{% if product.has_alc %}
{% set icon = (alc_icons | random) %}
{% else %}
{% set icon = (drink_icons | random) %}
{% endif %}
{% endif %}
<i class="fa-solid fa-{{ icon }} fa-3x" style="float: left"></i>
{{ product.name }}<br>
{% if config.SHOW_BALANCE_AND_PRICE %}
{{ macro.render_currency(product.price) }}
{% else %}
{{ macro.render_contingent(balance, product.price) }}
{% endif %}
</button>
{% endif %}
{% else %}
<button type="submit" class="btn btn-outline-primary py-5 ps-5" name="product_id"
{% endif %}
value="{{ product.id }}">
{% if product in favorites %}
{% if product in favorites %}
<button type="submit" class="btn btn-primary py-5 ps-5" name="product_id" value="{{ product.id }}">
{% set icon = "star" %}
{% elif product.is_food %}
{% set icon = (food_icons | random) %}
{% else %}
{% if product.has_alc %}
{% set icon = (alc_icons | random) %}
<i class="fa-solid fa-{{ icon }} fa-3x" style="float: left"></i>
{{ product.name }}<br>
{% if config.SHOW_BALANCE_AND_PRICE %}
{{ macro.render_currency(product.price) }}
{% else %}
{% set icon = (drink_icons | random) %}
{{ macro.render_contingent(balance, product.price) }}
{% endif %}
{% endif %}
<i class="fa-solid fa-{{ icon }} fa-3x" style="float: left"></i>
{{ product.name }}<br>
{% if config.SHOW_BALANCE_AND_PRICE %}
{{ macro.render_currency(product.price) }}
</button>
{% else %}
{{ macro.render_contingent(balance, product.price) }}
<button type="submit" class="btn btn-outline-primary py-5 ps-5" name="product_id" value="{{ product.id }}">
{% if product.is_food %}
{% set icon = (food_icons | random) %}
{% else %}
{% if product.has_alc %}
{% set icon = (alc_icons | random) %}
{% else %}
{% set icon = (drink_icons | random) %}
{% endif %}
{% endif %}
<i class="fa-solid fa-{{ icon }} fa-3x" style="float: left"></i>
{{ product.name }}<br>
{% if config.SHOW_BALANCE_AND_PRICE %}
{{ macro.render_currency(product.price) }}
{% else %}
{{ macro.render_contingent(balance, product.price) }}
{% endif %}
</button>
{% endif %}
</button>
{% endif %}

</div>
{% endif %}
{% endfor %}
Expand Down
54 changes: 54 additions & 0 deletions nanposweb/templates/verify.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{% extends 'base.html' %}

{% block pagetitle %}Verify Purchase{% endblock %}

{% block utilbar %}
<div class="navbar navbar-light bg-light">
<div class="container row-cols-auto">
{% if impersonate_user is not none %}
<div class="d-flex align-items-center me-auto">
<i class="fa-solid fa-user-secret text-danger me-1"></i>
<span class="text-danger me-3">Impersonating {{ impersonate_user.name }}</span>
<a role="button" class="btn btn-danger"
href="{{ url_for('admin.users.pop_impersonate') }}">Disable</a>
</div>
{% endif %}

<div class="row-cols-auto d-flex ms-auto">
<div class="mx-1">
<span class="nav-link text-dark">User: {{ user_name }}</span>
</div>
{% if config.SHOW_BALANCE_AND_PRICE %}
<div>
<span class="nav-link text-dark">Balance: {{ macro.render_currency(balance) }}</span>
</div>
{% endif %}
</div>

</div>
</div>
{% endblock %}

{% block content %}
<form action="{{ url_for('main.index') }}" method="post">
{{ form.csrf_token }}
<h1>Verify purchase</h1>
<p class="fs-3">Have you purchased this item, a {{ product.name }}, already at the other device?</p>

<div class="container">
<div class="row">
<div class="col">
<a class="btn btn-outline-secondary w-100 py-2" href="{{ url_for('main.index') }}">No</a>
</div>
<div class="col">
<button type="submit" class="btn btn-primary w-100 py-2" name="product_id" value="{{ product.id }}">Yes</button>
</div>
</div>
</div>
<br>
{% if config.VERIFY_FREE_PURCHASES_NOTE %}
<p>{{ config.VERIFY_FREE_PURCHASES_NOTE }}</p>
{% endif %}

</form>
{% endblock %}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ name = "nanposweb"
description = "A simple point of sale system for drinks"
requires-python= ">=3.9"
license = {text = "MIT"}
version = "1.1.7"
version = "1.2.0"
dependencies = [
"Flask==2.2.3",
"Flask-SQLAlchemy",
Expand Down

0 comments on commit 0b49709

Please sign in to comment.