From 5a73e1689bd427ec1a581e3c32024cc7a09c76e8 Mon Sep 17 00:00:00 2001 From: Holger Frey Date: Wed, 8 Apr 2020 14:40:37 +0200 Subject: [PATCH] working on orders --- ordr3/models.py | 6 +- ordr3/resources.py | 37 ++++++++- ordr3/scripts/migrate_db.py | 9 +++ ordr3/static/script.js | 51 +++++++++++- ordr3/static/style.css | 40 ++++++++-- ordr3/templates/layout_full.jinja2 | 35 +++++---- ordr3/templates/macros.jinja2 | 15 ++++ ordr3/templates/orders/list.jinja2 | 87 +++++++++++++++++++++ ordr3/templates/orders/list_content.jinja2 | 53 +++++++++++++ ordr3/templates/users/list_content.jinja2 | 2 +- ordr3/views/__init__.py | 17 ++++ ordr3/views/orders.py | 83 ++++++++++++++++++++ ordr3/views/users.py | 18 ++--- ordr3_backup.sqlite | Bin 2506752 -> 3149824 bytes tests/test_models.py | 4 +- 15 files changed, 411 insertions(+), 46 deletions(-) create mode 100644 ordr3/templates/orders/list.jinja2 create mode 100644 ordr3/templates/orders/list_content.jinja2 create mode 100644 ordr3/views/orders.py diff --git a/ordr3/models.py b/ordr3/models.py index 120e3ea..f8b664b 100644 --- a/ordr3/models.py +++ b/ordr3/models.py @@ -106,6 +106,10 @@ class OrderItem(Model): def total_price(self): return self.unit_price * self.amount + @property + def in_process(self): + return self.status not in (OrderStatus.OPEN, OrderStatus.HOLD) + def add_to_log(self, log_entry): """ adds a log item to the status log """ if len(self.log) == 0: @@ -178,7 +182,7 @@ class User(Model): @property def principal(self): - return f"user:{self.id}" + return f"user:{self.username}" @property def principals(self): diff --git a/ordr3/resources.py b/ordr3/resources.py index c67d063..8407276 100644 --- a/ordr3/resources.py +++ b/ordr3/resources.py @@ -62,13 +62,48 @@ class UserList(BaseResource): raise KeyError from e +class Order(BaseResource): + def __acl__(self): + """ access controll list """ + acl = [ + (Allow, "role:user", "view"), + (Allow, "role:purchaser", "edit"), + (Allow, "role:purchaser", "delete"), + ] + if self.model.in_process: + acl.append((Allow, "role:purchaser", "reorder")), + acl.append((Allow, "role:user", "reorder")), + else: + acl.append((Allow, f"user:{self.model.created_by}", "edit")) + acl.append((Allow, f"user:{self.model.created_by}", "delete")) + return acl + + @classmethod + def from_model(cls, model, parent): + """ initializes a resource from an SQLalchemy object """ + return cls(model.id, parent, model) + + +class OrderList(BaseResource): + def __acl__(self): + """ access controll list """ + return [ + (Allow, "role:user", "view"), + (Allow, "role:user", "add"), + (Allow, "role:purchaser", "edit-multiple"), + ] + + class Root(BaseResource): """ Root resource """ __name__ = None __parent__ = None - nodes = {"users": UserList} + nodes = { + "users": UserList, + "orders": OrderList, + } def __init__(self, request): self.request = request diff --git a/ordr3/scripts/migrate_db.py b/ordr3/scripts/migrate_db.py index a6c0467..3eba878 100644 --- a/ordr3/scripts/migrate_db.py +++ b/ordr3/scripts/migrate_db.py @@ -299,6 +299,15 @@ def migrate_orders(cursor, repo): ) order.add_to_log(log_entry) + if order.created_on < datetime(2018, 1, 1): + log_entry = models.LogEntry( + order.id, + models.OrderStatus.COMPLETED, + "OrderSystem", + datetime.utcnow(), + ) + order.add_to_log(log_entry) + def parse_args(argv): parser = argparse.ArgumentParser() diff --git a/ordr3/static/script.js b/ordr3/static/script.js index d49c41a..cfa2155 100644 --- a/ordr3/static/script.js +++ b/ordr3/static/script.js @@ -44,7 +44,21 @@ $(function() { } }); - $(".o3-reg-username-source").on("keyup", function(event){ + $(".o3-details").on("click", function(event){ + // handle detail visibility + var target = $(event.delegateTarget) + if (target.hasClass("o3-show-details")) { + target.removeClass("o3-show-details"); + target.addClass("o3-hide-details"); + $(".o3-data-table").addClass("o3-data-hide-details") + } else { + target.addClass("o3-show-details"); + target.removeClass("o3-hide-details"); + $(".o3-data-table").removeClass("o3-data-hide-details") + } + }); + + $(".o3-reg-username-source").on("keyup", function(event) { // automatic username creation var first_name = $(".o3-reg-firstname").val(); var last_name = $(".o3-reg-lastname").val(); @@ -52,18 +66,49 @@ $(function() { $(".o3-reg-username").val(user_name); }); + $("#o3-selectall").on("click", function(event) { + // select all checkboxes + var target = $(event.delegateTarget); + var is_checked = target.is(':checked'); + $(".o3-multiple-selection").prop('checked', is_checked); + if (is_checked) { + $(".o3-on-select").fadeIn(100); + } else { + $(".o3-on-select").fadeOut(100); + } + }); + + $(".o3-data-table").on("click", function(event) { + // set visibility of edit multiples + if ($(".o3-data-table .o3-multiple-selection:checked").length > 0) { + $(".o3-on-select").fadeIn(100); + } else { + $(".o3-on-select").fadeOut(100); + } + }); + + $(".o3-on-select .btn").on("click", function(event) { + var target = $(event.delegateTarget); + var action = target.attr("data-action"); + var form = $("#o3-multiple-form"); + form.attr("action", action); + form.submit(); + }); $(".o3-copy").on("click", function(event) { - var target = $(event.delegateTarget) + // copy to clipboard + var target = $(event.delegateTarget); var $temp = $(""); $("body").append($temp); $temp.val($(target).html()).select(); document.execCommand("copy"); $temp.remove(); - $(target).fadeOut( 100 ).delay( 100 ).fadeIn( 100 ); + //$(target).fadeOut( 100 ).delay( 100 ).fadeIn( 100 ); + $(target).fadeTo(100, 0).delay(100).fadeTo(100, 1); }); $(".o3-confirmation").on("click", function(event) { + // delete confirmation dialog var target = $(event.delegateTarget); var checkbox = $(target).find(".custom-control-input"); var button = $(".o3-confirm-button"); diff --git a/ordr3/static/style.css b/ordr3/static/style.css index 4aa6b5f..57cb014 100644 --- a/ordr3/static/style.css +++ b/ordr3/static/style.css @@ -39,14 +39,18 @@ margin-top:2em; } +.o3-on-select { + display:none; +} + .o3-details.o3-show-details .bi-eye-slash, .o3-details.o3-hide-details .bi-eye { - display:none; + display:inline; } .o3-details.o3-hide-details .bi-eye-slash, .o3-details.o3-show-details .bi-eye { - display:inline; + display:none; } .o3-users-link:hover .badge.badge-light{ @@ -109,7 +113,7 @@ background-color: #f8f9fa!important; } -.o3-content .table td { +.o3-content .table td, .o3-content .table th { padding:.5rem; } @@ -127,11 +131,23 @@ td.o3-actions a:hover { cursor:pointer; } - .o3-data-table th { border-top:none; } +.o3-data-table .form-control { + margin-bottom:.2rem; + margin-right:.2rem; + } + +.o3-data-table.o3-data-hide-details .text-secondary { + display:none; + } + +.o3-data-table .badge { + text-transform: uppercase; +} + .infinite-more-link td { text-align:center; @@ -150,18 +166,26 @@ td.o3-actions a:hover { color: #fff; border-color: #dc3545; background-color:#dc3545; -} + } .o3-delete-warning .custom-control-input:focus ~ .custom-control-label::before { box-shadow:0 0 0 .2rem rgba(0, 123, 255, .25) -} + } .o3-delete-warning .custom-control-input:focus:not(:checked) ~ .custom-control-label::before { border-color:#80bdff -} + } .o3-delete-warning .custom-control-input:not(:disabled):active ~ .custom-control-label::before { color: #fff; background-color: #b3d7ff; border-color:#b3d7ff -} + } + +.o3-dont-wrap { + word-break: keep-all; + white-space: nowrap; + } + + + diff --git a/ordr3/templates/layout_full.jinja2 b/ordr3/templates/layout_full.jinja2 index aac59a9..7995093 100644 --- a/ordr3/templates/layout_full.jinja2 +++ b/ordr3/templates/layout_full.jinja2 @@ -30,28 +30,29 @@ {{ macros.icon("plus-circle") }} - {% endif %} - {% if is_user_list %} - - {% elif request.user.role.name == "ADMIN" %} + + {% if request.user.role.name == "ADMIN" %}