Browse Source

added batch edit and delete of orders

funding-tag
Holger Frey 5 years ago
parent
commit
352b8e3509
  1. 2
      ordr3/events.py
  2. 10
      ordr3/repo.py
  3. 11
      ordr3/resources.py
  4. 5
      ordr3/services.py
  5. 8
      ordr3/static/script.js
  6. 8
      ordr3/static/style.css
  7. 16
      ordr3/templates/emails/status_change.jinja2
  8. 6
      ordr3/templates/layout_full.jinja2
  9. 80
      ordr3/templates/orders/batch_delete.jinja2
  10. 85
      ordr3/templates/orders/batch_edit.jinja2
  11. 51
      ordr3/templates/orders/delete.jinja2
  12. 30
      ordr3/templates/orders/list.jinja2
  13. 12
      ordr3/templates/orders/list_content.jinja2
  14. 2
      ordr3/templates/users/delete.jinja2
  15. 197
      ordr3/views/orders.py
  16. 6
      ordr3/views/users.py
  17. 4
      tests/test_services.py

2
ordr3/events.py

@ -57,7 +57,7 @@ class OrderStatusChangeEmail(EmailNotification): @@ -57,7 +57,7 @@ class OrderStatusChangeEmail(EmailNotification):
""" user notification for order status change """
subject = "[ordr] Order Status Change"
template = "ordr3:templates/emails/order.jinja2"
template = "ordr3:templates/emails/status_change.jinja2"
class PasswordResetEmail(EmailNotification):

10
ordr3/repo.py

@ -28,6 +28,10 @@ class AbstractOrderRepository(abc.ABC): @@ -28,6 +28,10 @@ class AbstractOrderRepository(abc.ABC):
def get_order(self, reference):
""" get an order from the datastore by primary key """
@abc.abstractmethod
def delete_order(self, order):
""" remove an order from the datastore """
@abc.abstractmethod
def list_orders(self):
""" list orders orderd by date, descending """
@ -94,6 +98,12 @@ class SqlAlchemyRepository(AbstractOrderRepository): @@ -94,6 +98,12 @@ class SqlAlchemyRepository(AbstractOrderRepository):
""" add an order to the database """
self._add_item_to_db(order)
def delete_order(self, order):
""" remove an order from the datastore """
for log_entry in order.log:
self.session.delete(log_entry)
self._delete_item_from_db(order)
def get_order(self, reference):
""" get an order from the database by primary key """
try:

11
ordr3/resources.py

@ -90,9 +90,18 @@ class OrderList(BaseResource): @@ -90,9 +90,18 @@ class OrderList(BaseResource):
return [
(Allow, "role:user", "view"),
(Allow, "role:user", "add"),
(Allow, "role:purchaser", "edit-multiple"),
(Allow, "role:purchaser", "batch-edit"),
(Allow, "role:purchaser", "batch-delete"),
]
def __getitem__(self, key):
""" returns child resources """
try:
order = self.request.repo.get_order(key)
return Order.from_model(order, self)
except StopIteration as e:
raise KeyError from e
class Root(BaseResource):
""" Root resource """

5
ordr3/services.py

@ -56,9 +56,14 @@ def _find_consumables(repo, repeat=3, days=365 * 2): @@ -56,9 +56,14 @@ def _find_consumables(repo, repeat=3, days=365 * 2):
def create_log_entry(order, status, user):
old_status = order.status
log_entry = models.LogEntry(order.id, status, user.username)
order.add_to_log(log_entry)
# is this noteworthy?
return (old_status != status) and (order.created_by != user.username)
def verify_credentials(repo, pass_ctx, username, password):
try:

8
ordr3/static/script.js

@ -123,6 +123,14 @@ $(function() { @@ -123,6 +123,14 @@ $(function() {
}
});
$(".o3-set-multi-status").on("click", function(event) {
// set status to multiple items
var target = $(event.delegateTarget);
var selects = $(".o3-data-edit-multiple select");
console.log(target.attr("data-multi-value"))
selects.val(target.attr("data-multi-value"))
});
var infinite = new Waypoint.Infinite({
element: $('.infinite-container')[0]
});

8
ordr3/static/style.css

@ -187,5 +187,9 @@ td.o3-actions a:hover { @@ -187,5 +187,9 @@ td.o3-actions a:hover {
white-space: nowrap;
}
.o3-data-edit-multiple td {
vertical-align:middle;
}
.o3-data-edit-multiple select {
width: 7rem;
}

16
ordr3/templates/emails/status_change.jinja2

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
Dear {{ user.first_name }},
The status of one or more of your orders have been changed by {{ request.user.first_name }}:
{% for order in data %}
{{ order.cas_description }}
- new status: {{ order.status.name|title }}
{% endfor %}
You can now log in at {{ request.resource_url(request.root) }} and order some more stuff.
Regards,
The Ordr System
--
Please don't respont to this email! This is an automatically generated notification.

6
ordr3/templates/layout_full.jinja2

@ -32,10 +32,10 @@ @@ -32,10 +32,10 @@
</li>
<li class="nav-item o3-on-select mr-3">
<div class="btn-group">
<button class="btn btn-outline-light pt-1" title="Edit selected orders" data-action="{{ request.resource_url(context, "edit-multiple") }}">
{{ macros.icon("pencil-square") }}
<button class="btn btn-outline-light pt-1" title="Change Status" data-action="{{ request.resource_url(context, "batch-edit") }}">
{{ macros.icon("flag") }}
</button>
<button class="btn btn-outline-light pt-1" title="Delete selected orders" data-action="{{ request.resource_url(context, "delete-multiple") }}">
<button class="btn btn-outline-light pt-1" title="Delete" data-action="{{ request.resource_url(context, "batch-delete") }}">
{{ macros.icon("trash") }}
</button>
</div>

80
ordr3/templates/orders/batch_delete.jinja2

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
{% extends "ordr3:templates/layout_full.jinja2" %}
{% block subtitle %} Delete {{orders|length}} Orders {% endblock subtitle %}
{% block content %}
<div class="col-10">
<div class="border border-danger rounded o3-delete-warning">
<h4 class="mb-2 text-muted mb-4 text-truncate">Delete {{orders|length}} Orders <span class="text-danger">{{ context.model.cas_description }}</span></h4>
<p>You are about to delete the following orders:</p>
<form action="{{request.resource_url(context, 'batch-delete-confirm')}}" method="POST">
<table class="table table-hover o3-data-table o3-data-hide-details">
<thead>
<tr>
<th scope="col" class="o3-dont-wrap">
Date
</th>
<th scope="col" class="o3-dont-wrap">
CAS / Description
</th>
<th scope="col" class="o3-dont-wrap">
Vendor
</th>
<th scope="col" class="o3-dont-wrap text-right">
Total Price
</th>
<th scope="col" class="o3-dont-wrap">
Ordered by
</th>
<th scope="col" class="o3-dont-wrap">
Status
</th>
</tr>
</thead>
<tbody>
{% for order in orders %}
<tr>
<td class="o3-dont-wrap">
<input type="hidden" name="selection" value="{{ order.id }}">
{{ order.created_on.strftime("%Y-%m-%d") }}
</td>
<td>
{{ order.cas_description }}
</td>
<td>
{{ order.vendor }}
</td>
<td class="text-right">
<span class="o3-dont-wrap">{{ "%.2f"|format(order.total_price)|replace(".", ",") }}</a> {{ order.currency }}</span>
</td>
<td>
{{ order.created_by }}
</td>
<td>
{{ macros.status_label(order.status) }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p class="font-weight-bold mt-4 mb-4">This action is permanent and cannot be undone!</p>
<p class="mt-4">
<div class="custom-control custom-switch o3-confirmation">
<input type="checkbox" class="custom-control-input" id="confirmation" value="confirmed" name="confirmation">
<label class="custom-control-label" for="confirmation">I confirm that I want to delete this order.</label>
</div>
</p>
<p class="mt-4">
<input type="hidden" name="csrf_token" value="{{csrf_token}}">
<button class="btn btn-outline-danger o3-confirm-button" type="submit" name="delete" disabled="disabled">Delete Orders</button>
<button class="btn btn-outline-secondary" type="submit" name="cancel">Cancel</button>
</p>
</form>
</div>
</div>
{% endblock content %}

85
ordr3/templates/orders/batch_edit.jinja2

@ -0,0 +1,85 @@ @@ -0,0 +1,85 @@
{% extends "ordr3:templates/layout_full.jinja2" %}
{% block subtitle %} Edit {{orders|length}} Orders {% endblock subtitle %}
{% block content %}
<div class="col-10">
<h4 class="mb-2 text-muted mb-4 text-truncate">Edit {{orders|length}} Orders <span class="text-danger">{{ context.model.cas_description }}</span></h4>
<form action="{{request.resource_url(context, 'batch-edit-confirm')}}" method="POST">
<table class="table table-hover o3-data-table o3-data-edit-multiple">
<thead>
<tr>
<th scope="col" class="o3-dont-wrap">
Date
</th>
<th scope="col" class="o3-dont-wrap">
CAS / Description
</th>
<th scope="col" class="o3-dont-wrap">
Vendor
</th>
<th scope="col" class="o3-dont-wrap text-right">
Total Price
</th>
<th scope="col" class="o3-dont-wrap">
Ordered by
</th>
<th scope="col" class="o3-dont-wrap">
Status
</th>
</tr>
</thead>
<tbody>
{% for order in orders %}
<tr>
<td class="o3-dont-wrap">
{{ order.created_on.strftime("%Y-%m-%d") }}
</td>
<td>
{{ order.cas_description }}
</td>
<td>
{{ order.vendor }}
</td>
<td class="text-right">
<span class="o3-dont-wrap">{{ "%.2f"|format(order.total_price)|replace(".", ",") }}</a> {{ order.currency }}</span>
</td>
<td>
{{ order.created_by }}
</td>
<td>
<input type="hidden" name="selection" value="{{ order.id }}">
<select name="status-{{ order.id }}" class="custom-select custom-select-sm">
{% for status in stati %}
<option value="{{status.name}}" {% if order.status == status %}selected="selected"{% endif %}>{{status.name|capitalize}}</option>
{% endfor %}
</select>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p class="mt-4">
<input type="hidden" name="csrf_token" value="{{csrf_token}}">
<div class="btn-group">
<button type="button" class="btn btn-primary o3-set-multi-status" data-multi-value="APPROVAL">Set all to Approval</button>
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" data-reference="parent">
<span class="small">{{ macros.icon("caret-down-fill") }}</span>
</button>
<div class="dropdown-menu">
<a class="dropdown-item o3-set-multi-status" data-multi-value="ORDERED">Set all to Ordered</a>
<a class="dropdown-item o3-set-multi-status" data-multi-value="COMPLETED">Set all to Completed</a>
<a class="dropdown-item o3-set-multi-status" data-multi-value="HOLD">Set all to Hold</a>
</div>
</div>
<button class="btn btn-danger" type="submit" name="change">Change Orders</button>
<button class="btn btn-outline-secondary" type="submit" name="cancel">Cancel</button>
</p>
</form>
</div>
{% endblock content %}

51
ordr3/templates/orders/delete.jinja2

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
{% extends "ordr3:templates/layout_full.jinja2" %}
{% block subtitle %} Delete Order {{context.model.cas_description}} {% endblock subtitle %}
{% block content %}
<div class="col-5">
<div class="border border-danger rounded o3-delete-warning">
<h4 class="mb-2 text-muted mb-4 text-truncate">Delete Order <span class="text-danger">{{ context.model.cas_description }}</span></h4>
<p>You are about to delete the following order:</p>
<dl class="row">
<dt class="col-sm-4">Cas / Description</dt>
<dd class="col-sm-8">{{ context.model.cas_description }}</dd>
<dt class="col-sm-4">Vendor</dt>
<dd class="col-sm-8">{{ context.model.vendor }}</dd>
<dt class="col-sm-4">Catalog Nr.</dt>
<dd class="col-sm-8">{{ context.model.catalog_nr }}</dd>
<dt class="col-sm-4">Package Size</dt>
<dd class="col-sm-8">{{ context.model.package_size }}</dd>
<dt class="col-sm-4">Amount, Price</dt>
<dd class="col-sm-8">{{ context.model.amount }} * {{ context.model.unit_price }} {{ context.model.currency }}</dd>
<dt class="col-sm-4">Ordered By</dt>
<dd class="col-sm-8">{{ context.model.created_by }}</dd>
<dt class="col-sm-4">Ordered On</dt>
<dd class="col-sm-8">{{ context.model.created_on.strftime("%Y-%m-%d %H:%I") }}</dd>
<dt class="col-sm-4">Status</dt>
<dd class="col-sm-8">{{ macros.status_label(context.model.status) }}</dd>
</dl>
<p class="font-weight-bold mt-4 mb-4">This action is permanent and cannot be undone!</p>
<form action="{{request.resource_url(context, 'delete')}}" method="POST">
<p class="mt-4">
<div class="custom-control custom-switch o3-confirmation">
<input type="checkbox" class="custom-control-input" id="confirmation" value="confirmed" name="confirmation">
<label class="custom-control-label" for="confirmation">I confirm that I want to delete this order.</label>
</div>
</p>
<p class="mt-4">
<input type="hidden" name="csrf_token" value="{{csrf_token}}">
<button class="btn btn-outline-danger o3-confirm-button" type="submit" name="delete" disabled="disabled">Delete Order</button>
<button class="btn btn-outline-secondary" type="submit" name="cancel">Cancel</button>
</p>
</form>
</div>
</div>
<div class="col-5"></div>
{% endblock content %}

30
ordr3/templates/orders/list.jinja2

@ -1,13 +1,13 @@ @@ -1,13 +1,13 @@
{% extends "ordr3:templates/layout_full.jinja2" %}
{% block subtitle %} Manage Users {% endblock subtitle %}
{% block subtitle %} Orders {% endblock subtitle %}
{% block sidebar %}
<nav class="nav nav-pills flex-column">
<div class="nav-link disabled text-small" tabindex="-1" aria-disabled="true">All Orders</div>
<a class="nav-link {% if query_defaults['status'] == 'all' and not query_defaults['user'] %}active{% endif %}" href="{{ request.resource_url(context, query=query_defaults(status=None, user=None, search=None)) }}">All</a>
<a class="nav-link {% if query_defaults['status'] == 'all' and not query_defaults['user'] and not query_defaults['search'] %}active{% endif %}" href="{{ request.resource_url(context, query=query_defaults(status=None, user=None, search=None)) }}">All</a>
{% for status in stati %}
<a class="nav-link {% if query_defaults['status'] == status.name.lower() and not query_defaults['user'] %}active{% endif %}" href="{{ request.resource_url(context, query=query_defaults(status=status.name.lower(), user=None, search=None)) }}">{{status.name.lower()}}</a>
{% endfor %}
@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
{% endfor %}
</nav>
{% if request.has_permission("edit-multiple", context) %}
{% if request.has_permission("batch-edit", context) %}
<nav class="nav nav-pills flex-column mt-3">
<div class="nav-link disabled text-small" tabindex="-1" aria-disabled="true">Specials</div>
<a class="nav-link {% if query_defaults['user'] == '-purchaser-' %}active{% endif %}" href="{{ request.resource_url(context, query=query_defaults(status=None, user='-purchaser-', search=None)) }}">Edited by me</a>
@ -39,34 +39,34 @@ @@ -39,34 +39,34 @@
<table class="table table-hover o3-data-table o3-data-hide-details">
<thead>
<tr>
{% if request.has_permission("edit-multiple", context) %}
{% if is_order_list and request.has_permission("batch-edit", context) %}
<th scope="col"><input type="checkbox" id="o3-selectall"></th>
{% endif %}
<th scope="col">
<th scope="col" class="o3-dont-wrap">
Date
<div class="text-secondary small">Time</div>
</th>
<th scope="col">
<th scope="col" class="o3-dont-wrap">
CAS / Description
<div class="text-secondary small">Catalog Nr.</div>
<div class="text-secondary small">Package Size</div>
</th>
<th scope="col">
<th scope="col" class="o3-dont-wrap">
Vendor
<div class="text-secondary small">Account</div>
<div class="text-secondary small">Catalog Nr.</div>
</th>
<th scope="col" class="o3-dont-wrap text-right">
Total Price
<div class="text-secondary small">Unit Price</div>
</th>
<th scope="col">
Status
<div class="text-secondary small">&nbsp;</div>
</th>
<th scope="col">
<th scope="col" class="o3-dont-wrap">
Ordered by
<div class="text-secondary small">Account</div>
</th>
<th scope="col" class="o3-dont-wrap">
Status
<div class="text-secondary small">&nbsp;</div>
</th>
<th scope="col">
<th scope="col" class="o3-dont-wrap">
Actions
<div class="text-secondary small">&nbsp;</div>
</th>

12
ordr3/templates/orders/list_content.jinja2

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
{% import 'ordr3:templates/macros.jinja2' as macros with context %}
{% for order in orders %}
<tr class="infinite-item">
{% if request.has_permission("edit-multiple", context) %}
{% if is_order_list and request.has_permission("batch-edit", context) %}
<td><input type="checkbox" name="selection" value="{{order.model.id}}" class="o3-multiple-selection"></td>
{% endif %}
<td class="o3-dont-wrap">
@ -10,22 +10,22 @@ @@ -10,22 +10,22 @@
</td>
<td>
<a class="o3-copy" title="copy to clipboard">{{ order.model.cas_description }}</a>
<div class="text-secondary small"><a class="o3-copy" title="copy to clipboard">{{ order.model.catalog_nr }}</a></div>
<div class="text-secondary small"><a class="o3-copy">{{ order.model.package_size }}</a></div>
</td>
<td>
<a class="o3-copy" title="copy to clipboard">{{ order.model.vendor }}</a>
<div class="text-secondary small"><a class="o3-copy" title="copy to clipboard">{{ order.model.account }}</a></div>
<div class="text-secondary small"><a class="o3-copy" title="copy to clipboard">{{ order.model.catalog_nr }}</a></div>
</td>
<td class="text-right">
<span class="o3-dont-wrap"><a class="o3-copy" title="copy to clipboard">{{ "%.2f"|format(order.model.total_price)|replace(".", ",") }}</a> {{ order.model.currency }}</span>
<div class="text-secondary small o3-dont-wrap"><a class="o3-copy" title="copy to clipboard">{{ order.model.amount }}</a> * <a class="o3-copy" title="copy to clipboard">{{ "%.2f"|format(order.model.unit_price)|replace(".", ",") }}</a> {{ order.model.currency }}</div>
</td>
<td>
{{ macros.status_label(order.model.status) }}
<div class="text-secondary small">&nbsp;</div>
<a href="{{ request.resource_url(context, query={'user':order.model.created_by})}}" title="Show orders for this user">{{ order.model.created_by}}</a>
<div class="text-secondary small"><a class="o3-copy" title="copy to clipboard">{{ order.model.account }}</a></div>
</td>
<td>
<a href="{{ request.resource_url(context, query={'user':order.model.created_by})}}" title="Show orders for this user">{{ order.model.created_by}}</a>
{{ macros.status_label(order.model.status) }}
<div class="text-secondary small">&nbsp;</div>
</td>
<td class="o3-actions">

2
ordr3/templates/users/delete.jinja2

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
{% extends "ordr3:templates/layout_full.jinja2" %}
{% block subtitle %} Edit User {{context.model.user_name}} {% endblock subtitle %}
{% block subtitle %} Delete User {{context.model.user_name}} {% endblock subtitle %}
{% block content %}

197
ordr3/views/orders.py

@ -1,13 +1,11 @@ @@ -1,13 +1,11 @@
# import deform
from sqlalchemy import or_ # , func
# from pyramid.csrf import get_csrf_token
from pyramid.csrf import get_csrf_token
from pyramid.view import view_config
from pyramid.httpexceptions import HTTPFound
from . import DefaultQueryParams, get_offset
from .. import models, resources # , events, services
# from pyramid.httpexceptions import HTTPFound
from .. import events, models, services, resources
# from ..schemas import account
@ -20,6 +18,16 @@ def get_status(request): @@ -20,6 +18,16 @@ def get_status(request):
return None
def get_multiple_orders(context, request):
order_ids = {v for k, v in request.POST.items() if k == "selection"}
return (
request.repo.session.query(models.OrderItem)
.filter(models.OrderItem.id.in_(order_ids))
.order_by(models.OrderItem.created_on.desc())
.all()
)
@view_config(
context="ordr3:resources.OrderList",
permission="view",
@ -33,7 +41,7 @@ def get_status(request): @@ -33,7 +41,7 @@ def get_status(request):
xhr=True,
renderer="ordr3:templates/orders/list_content.jinja2",
)
def list(context, request):
def order_list(context, request):
limit = 25
offset = get_offset(request)
status = get_status(request)
@ -81,3 +89,180 @@ def list(context, request): @@ -81,3 +89,180 @@ def list(context, request):
"stati": models.OrderStatus,
"query_defaults": query_defaults,
}
@view_config(
context="ordr3:resources.OrderList",
name="batch-delete",
permission="batch-delete",
request_method="POST",
require_csrf=False,
renderer="ordr3:templates/orders/batch_delete.jinja2",
)
def batch_delete(context, request):
orders = get_multiple_orders(context, request)
if not orders:
return HTTPFound(request.resource_path(context))
if len(orders) > 100:
request.emit(
events.FlashMessage.warning(
f"You cannot delete more than 100 orders in one sweep."
)
)
return HTTPFound(request.resource_path(context))
return {"orders": orders, "csrf_token": get_csrf_token(request)}
@view_config(
context="ordr3:resources.OrderList",
name="batch-delete-confirm",
permission="batch-delete",
request_method="POST",
)
def batch_delete_confirmed(context, request):
if "delete" not in request.POST:
return HTTPFound(request.resource_path(context.__parent__))
if request.POST.get("confirmation", "") != "confirmed":
return HTTPFound(request.resource_path(context.__parent__))
orders = get_multiple_orders(context, request)
if not orders:
return HTTPFound(request.resource_path(context))
if len(orders) > 100:
request.emit(
events.FlashMessage.warning(
f"You cannot delete more than 100 orders in one sweep."
)
)
else:
for order in orders:
request.repo.delete_order(order)
request.emit(
events.FlashMessage.info(
f"{len(orders)} orders have been deleted."
)
)
return HTTPFound(request.resource_path(context))
@view_config(
context="ordr3:resources.OrderList",
name="batch-edit",
permission="batch-edit",
request_method="POST",
require_csrf=False,
renderer="ordr3:templates/orders/batch_edit.jinja2",
)
def batch_edit(context, request):
orders = get_multiple_orders(context, request)
if not orders:
return HTTPFound(request.resource_path(context))
if len(orders) > 100:
request.emit(
events.FlashMessage.warning(
f"You cannot edit more than 100 orders in one sweep."
)
)
return HTTPFound(request.resource_path(context))
return {
"orders": orders,
"stati": models.OrderStatus,
"csrf_token": get_csrf_token(request),
}
@view_config(
context="ordr3:resources.OrderList",
name="batch-edit-confirm",
permission="batch-edit",
request_method="POST",
require_csrf=False,
)
def batch_edit_confirm(context, request):
if "change" not in request.POST:
return HTTPFound(request.resource_path(context.__parent__))
orders = get_multiple_orders(context, request)
print(request.POST)
print(orders)
if not orders:
return HTTPFound(request.resource_path(context))
if len(orders) > 100:
request.emit(
events.FlashMessage.warning(
f"You cannot edit more than 100 orders in one sweep."
)
)
return HTTPFound(request.resource_path(context))
changes = {}
for order in orders:
status_name = request.POST.get(f"status-{order.id}")
try:
status = models.OrderStatus[status_name.upper()]
is_noteworthy = services.create_log_entry(
order, status, request.user
)
if is_noteworthy:
if order.created_by not in changes:
changes[order.created_by] = []
changes[order.created_by].append(order)
except KeyError:
pass
for username, user_orders in changes.items():
user = request.repo.get_user_by_username(username)
request.emit(events.OrderStatusChangeEmail(user, user_orders))
if len(orders):
request.emit(
events.FlashMessage.info(
f"{len(orders)} orders have been updated."
)
)
return HTTPFound(request.resource_path(context))
@view_config(
context="ordr3:resources.Order",
permission="delete",
name="delete",
request_method="GET",
renderer="ordr3:templates/orders/delete.jinja2",
)
def delete_order(context, request):
return {"csrf_token": get_csrf_token(request)}
@view_config(
context="ordr3:resources.Order",
permission="delete",
name="delete",
request_method="POST",
)
def delete_confirmed(context, request):
if "delete" not in request.POST:
return HTTPFound(request.resource_path(context.__parent__))
if request.POST.get("confirmation", "") != "confirmed":
return HTTPFound(request.resource_path(context.__parent__))
request.emit(
events.FlashMessage.info(
f"The order {context.model.cas_description} has been deleted."
)
)
request.repo.delete_order(context.model)
return HTTPFound(request.resource_path(context.__parent__))

6
ordr3/views/users.py

@ -30,7 +30,7 @@ def get_role(request): @@ -30,7 +30,7 @@ def get_role(request):
xhr=True,
renderer="ordr3:templates/users/list_content.jinja2",
)
def list(context, request):
def user_list(context, request):
role = get_role(request)
offset = get_offset(request)
limit = 25
@ -149,8 +149,8 @@ def delete_confirmed(context, request): @@ -149,8 +149,8 @@ def delete_confirmed(context, request):
user = context.model
request.emit(
events.FlashMessage.warning(
f"The user {user.first_name} {user.last_name} is deleted."
events.FlashMessage.info(
f"The user {user.first_name} {user.last_name} has been deleted."
)
)
request.repo.delete_user(user)

4
tests/test_services.py

@ -22,6 +22,10 @@ class FakeOrderRepository(AbstractOrderRepository): @@ -22,6 +22,10 @@ class FakeOrderRepository(AbstractOrderRepository):
""" retrieve an order from the datastore """
return next(o for o in self._orders if o.id == reference)
def delete_order(self, order):
""" removes a user from the datastore """
self._orders.remove(order)
def list_orders(self):
""" list orders, sorted by descending creation date """
return sorted(self._orders, reverse=True, key=lambda x: x.created_on)

Loading…
Cancel
Save