Browse Source

adding orders and consumables work

funding-tag
Holger Frey 5 years ago
parent
commit
3e0d879c72
  1. 8
      ordr3/repo.py
  2. 4
      ordr3/schemas/orders.py
  3. 9
      ordr3/services.py
  4. 97
      ordr3/static/script.js
  5. 50
      ordr3/static/style.css
  6. 46
      ordr3/templates/orders/add.jinja2
  7. 69
      ordr3/views/orders.py

8
ordr3/repo.py

@ -123,6 +123,14 @@ class SqlAlchemyRepository(AbstractOrderRepository): @@ -123,6 +123,14 @@ class SqlAlchemyRepository(AbstractOrderRepository):
.all()
)
def list_consumables(self, limit_date, statuses):
return (
self.session.query(models.OrderItem)
.filter(models.OrderItem.created_on > limit_date)
.filter(models.OrderItem.status.in_(statuses))
.all()
)
def add_user(self, user):
""" add a user to the database """
self._add_item_to_db(user)

4
ordr3/schemas/orders.py

@ -213,6 +213,8 @@ class AddOrderSchema(CSRFSchema): @@ -213,6 +213,8 @@ class AddOrderSchema(CSRFSchema):
@classmethod
def as_form(cls, request, **override):
""" returns the schema as a form """
vendor_autocorrect_url = override.pop("autocorrect_url")
settings = {
"buttons": ("Place Order", "Cancel"),
"css_class": "deform o3-col-form o3-order-form",
@ -221,7 +223,7 @@ class AddOrderSchema(CSRFSchema): @@ -221,7 +223,7 @@ class AddOrderSchema(CSRFSchema):
form = super().as_form(request, **settings)
# set the url for vendor check
vendor_url = request.resource_url(request.context.__parent__, "vendor")
vendor_url = vendor_autocorrect_url
vendor_widget = form["item"]["vendor"].widget
vendor_widget.attributes["data-url"] = vendor_url

9
ordr3/services.py

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import uuid
import hashlib
from datetime import datetime
from datetime import datetime, timedelta
from collections import namedtuple
from urllib.parse import urlparse
@ -40,11 +40,8 @@ def find_consumables(repo, repeat=3, days=365 * 2): @@ -40,11 +40,8 @@ def find_consumables(repo, repeat=3, days=365 * 2):
def _find_consumables(repo, repeat=3, days=365 * 2):
""" helper function for find_consumables() implementation """
now = datetime.now()
by_date = (
o for o in repo.list_orders() if (now - o.created_on).days < days
)
relevant = (o for o in by_date if o.status in CONSUMABLE_STATI)
limit_date = datetime.now() - timedelta(days=days)
relevant = repo.list_consumables(limit_date, CONSUMABLE_STATI)
counter = {}
for order in relevant:
item = counter.setdefault(

97
ordr3/static/script.js

@ -6,6 +6,16 @@ var capitalize = function(some_string) { @@ -6,6 +6,16 @@ var capitalize = function(some_string) {
}
};
var copy_with_fade = function(event) {
var target = $(event.delegateTarget);
var $temp = $("<input>");
$("body").append($temp);
$temp.val($(target).html()).select();
document.execCommand("copy");
$temp.remove();
$(target).fadeTo(100, 0).delay(100).fadeTo(100, 1);
}
var load_more = function(target) {
var next = target.attr("data-next")
var href = new URL(window.location);
@ -106,13 +116,7 @@ $(function() { @@ -106,13 +116,7 @@ $(function() {
$(".o3-copy").on("click", function(event) {
// copy to clipboard
var target = $(event.delegateTarget);
var $temp = $("<input>");
$("body").append($temp);
$temp.val($(target).html()).select();
document.execCommand("copy");
$temp.remove();
$(target).fadeTo(100, 0).delay(100).fadeTo(100, 1);
copy_with_fade(event);
});
$(".o3-confirmation").on("click", function(event) {
@ -218,7 +222,84 @@ $(function() { @@ -218,7 +222,84 @@ $(function() {
});
});
$(".o3-add-new-order .item-cas_description input").on("keyup", function(event){
var target = $(event.delegateTarget);
var search = target.val().toLowerCase();
var search_length = search.length;
if (search == "") {
$(".o3-consumables table tbody tr").each( function( index, element ){
var row = $(element)
var text_elm = row.find(".o3-consumable-cas .text-truncate");
var pure_text = text_elm.text()
text_elm.html(pure_text);
row.show();
});
} else {
$(".o3-consumables table tbody tr").each( function( index, element ){
var row = $(element)
var cas = row.attr("title").toLowerCase()
var start = cas.indexOf(search);
var display = (start !== -1)
var text_elm = row.find(".o3-consumable-cas .text-truncate");
var pure_text = text_elm.text()
row.toggle(display)
if (display) {
var head = pure_text.substring(0, start);
var center = pure_text.substring(start, start+search_length);
var tails = pure_text.substring(start+search_length, pure_text.length);
var new_html = head + '<span class="font-weight-bolder">' + center + "</span>" + tails;
text_elm.html(new_html);
} else {
text_elm.html(pure_text);
}
});
}
});
$(".o3-consumable-category-filter").on("click", function(event) {
var target = $(event.delegateTarget);
var filter_category = target.attr("data-filter");
if (filter_category == "all") {
$(".o3-consumables table tbody tr").show();
} else {
$(".o3-consumables table tbody tr").each( function( index, element ){
var row = $(element)
var display = row.data("category") == filter_category;
row.toggle(display)
});
}
$(".o3-consumable-category-filter").removeClass("active");
target.addClass("active");
return false;
});
$(".o3-consumables table tbody tr").on("click", function(event) {
var target = $(event.delegateTarget);
var data_cas = target.attr("title");
var data_category = target.attr("data-category");
var data_catalog = target.attr("data-catalog");
var data_vendor = target.attr("data-vendor");
var data_size = target.attr("data-size");
var data_price = target.attr("data-price");
$(".item-cas_description input").val(data_cas);
$(".item-category select").val(data_category);
$(".item-catalog_nr input").val(data_catalog);
$(".item-vendor input").val(data_vendor);
$(".item-package_size input").val(data_size);
$(".item-unit_price .item-amount input").val(data_price);
update_price();
$(".item-cas_description input").focus()
});
var infinite = new Waypoint.Infinite({
element: $('.infinite-container')[0]
element: $('.infinite-container')[0],
onAfterPageLoad: function(items) {
items.find(".o3-copy").on("click", function(event) {
copy_with_fade(event);
});
}
});
});

50
ordr3/static/style.css

@ -194,21 +194,24 @@ td.o3-actions a:hover { @@ -194,21 +194,24 @@ td.o3-actions a:hover {
width: 7rem;
}
.o3-set-multi-status {
cursor:pointer;
}
a.d-inline-block.text-truncate {
max-width:100%;
}
}
.o3-content .table.o3-log-table th, .o3-content .table.o3-log-table td {
padding-left:0px;
padding-top:0px;
}
}
.o3-col-form .form-group.row {
padding-right:15px;
}
}
span.item-amount {
padding-left:0px;
@ -243,5 +246,42 @@ label.o3-form-copy { @@ -243,5 +246,42 @@ label.o3-form-copy {
label.o3-form-copy:hover {
text-decoration:underline;
}
}
.o3-consumables .nav {
border-bottom:1px solid #ced4da!important;
margin-bottom:1rem;
margin-top:2rem;
color:#6c757d!important;
}
.o3-consumables .nav li {
margin-top:.5rem
}
.o3-consumables .nav a {
}
.o3-consumables .nav a.active {
color:#343a40!important;
text-decoration:underline;
}
.o3-consumables table {
table-layout: fixed;
}
.o3-consumables table th {
border-top:0px;
}
.o3-consumables table tbody tr {
cursor: pointer;
}
.o3-consumables table .o3-consumable-cas {
width:75%;
}
.o3-consumables table .o3-consumable-cas .text-truncate {
display:block;
}

46
ordr3/templates/orders/add.jinja2

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
{% extends "ordr3:templates/layout_full.jinja2" %}
{% block subtitle %} Place a New Order {% endblock subtitle %}
{% block content %}
<div class="col-5 o3-add-new-order">
<h4 class="mb-2 mb-4 text-truncate">Place a New Order</h4>
{{form.render()|safe}}
</div>
<div class="col-5 o3-consumables">
<h4 class="mb-2 text-muted mb-4 text-truncate">Consumables</h4>
<ul class="nav">
<li class="nav-item">
<a class="nav-link active o3-consumable-category-filter text-secondary" href="#" data-filter="all">all</a>
</li>
{% for category in categories %}
<li class="nav-item">
<a class="nav-link o3-consumable-category-filter text-secondary" href="#" data-filter="{{ category.name }}">{{ category.name|title }}</a>
</li>
{% endfor %}
</ul>
<table class="table table-hover ">
<!--
<thead>
<tr>
<th scope="col">CAS / Description</th>
<th scope="col" class="text-right">Size</th>
</tr>
</thead>
-->
<tbody >
{% for con in consumables %}
<tr title="{{ con.cas_description }}" data-category="{{ con.category.name }}" data-catalog="{{ con.catalog_nr }}" data-vendor="{{ con.vendor }}" data-size="{{ con.package_size }}" data-price="{{ '%.2f'|format(con.unit_price) }}">
<td class="o3-consumable-cas"><span class="text-truncate">{{ con.cas_description }}</span></td>
<td class="text-nowrap text-right">{{ con.package_size }}</td>
{% endfor %}
</tbody>
</table>
</div>
{% endblock content %}

69
ordr3/views/orders.py

@ -303,6 +303,72 @@ def check_vendor_name(context, request): @@ -303,6 +303,72 @@ def check_vendor_name(context, request):
return result._asdict()
@view_config(
context="ordr3:resources.OrderList",
name="add",
permission="add",
request_method="GET",
renderer="ordr3:templates/orders/add.jinja2",
)
def new_order(context, request):
autocorrect_url = request.resource_url(context, "vendor")
form = orders.AddOrderSchema.as_form(
request, autocorrect_url=autocorrect_url
)
consumables = services.find_consumables(request.repo)
return {
"form": form,
"categories": models.OrderCategory,
"consumables": consumables,
}
@view_config(
context="ordr3:resources.OrderList",
name="add",
permission="add",
request_method="POST",
renderer="ordr3:templates/orders/add.jinja2",
)
def place_new_order(context, request):
""" process the reorder form """
autocorrect_url = request.resource_url(context, "vendor")
form = orders.AddOrderSchema.as_form(
request, autocorrect_url=autocorrect_url
)
if "Place_Order" not in request.POST:
return HTTPFound(request.resource_url(context.__parent__))
data = request.POST.items()
try:
appstruct = form.validate(data)
except deform.ValidationFailure:
consumables = services.find_consumables(request.repo)
return {
"form": form,
"categories": models.OrderCategory,
"consumables": consumables,
}
# form validation sucessful, change order
order = models.OrderItem(None, None, None, None, None, None, None, None)
update_order_with_form_data(order, appstruct)
services.create_log_entry(order, models.OrderStatus.OPEN, request.user)
request.repo.add_order(order)
request.emit(
events.FlashMessage.info(
f"The order {order.cas_description} has been placed."
)
)
return HTTPFound(request.resource_url(context.__parent__))
@view_config(
context="ordr3:resources.Order",
permission="view",
@ -422,8 +488,7 @@ def place_reorder(context, request): @@ -422,8 +488,7 @@ def place_reorder(context, request):
return {"form": form}
# form validation sucessful, change order
default = [None] * 8
order = models.OrderItem(*default)
order = models.OrderItem(None, None, None, None, None, None, None, None)
update_order_with_form_data(order, appstruct)
services.create_log_entry(order, models.OrderStatus.OPEN, request.user)

Loading…
Cancel
Save