diff --git a/ordr3/__init__.py b/ordr3/__init__.py index 7e92133..958977c 100644 --- a/ordr3/__init__.py +++ b/ordr3/__init__.py @@ -9,8 +9,6 @@ __version__ = "0.0.1" from pyramid.config import Configurator from pyramid.session import JSONSerializer, SignedCookieSessionFactory -from . import resources - def main(global_config, **settings): """ This function returns a Pyramid WSGI application. @@ -25,8 +23,6 @@ def main(global_config, **settings): require_csrf=settings["session.auto_csrf"] ) - config.set_root_factory(resources.Root) - config.include("pyramid_jinja2") config.include(".adapters") diff --git a/ordr3/routes.py b/ordr3/routes.py index 27f4da9..c9987c6 100644 --- a/ordr3/routes.py +++ b/ordr3/routes.py @@ -7,4 +7,4 @@ def includeme(config): settings = config.get_settings() age = int(settings.get("static_views.cache_max_age", 3600)) config.add_static_view("static", "static", cache_max_age=age) - # config.add_static_view('deform', 'deform:static', cache_max_age=age) + config.add_static_view("deform", "deform:static", cache_max_age=age) diff --git a/ordr3/schemas/__init__.py b/ordr3/schemas/__init__.py index afcf793..5028b0f 100644 --- a/ordr3/schemas/__init__.py +++ b/ordr3/schemas/__init__.py @@ -14,5 +14,6 @@ def includeme(config): ordr_templates = resource_filename("ordr3", "templates/deform") deform_templates = resource_filename("deform", "templates") search_path = (ordr_templates, deform_templates) + # search_path = (ordr_templates, ) Form.set_zpt_renderer(search_path) diff --git a/ordr3/schemas/orders.py b/ordr3/schemas/orders.py new file mode 100644 index 0000000..25f21d4 --- /dev/null +++ b/ordr3/schemas/orders.py @@ -0,0 +1,195 @@ +""" Schemas for form input and validation """ + +import deform +import colander + +from .base import CSRFSchema +from ..models import UserRole, OrderStatus, OrderCategory + +CATEGORIES = [(c.name, c.name.capitalize()) for c in OrderCategory] +STATI = [(s.name, s.name.capitalize()) for s in OrderStatus] + + +class OrderStatus(colander.Schema): + """ schema for editing order status + + parital schema, used in EditOrderSchema + """ + + status = colander.SchemaNode( + colander.String(), + widget=deform.widget.SelectWidget( + values=STATI, + css_class="custom-select col-sm-9", + item_css_class="row", + label_css_class="col-sm-3 col-form-label", + ), + ) + + +class OrderItem(colander.Schema): + """ schema for editing order information + + parital schema, used in NewOrderSchema and EditOrderSchema + """ + + cas_description = colander.SchemaNode( + colander.String(), + widget=deform.widget.TextInputWidget( + item_css_class="row", + label_css_class="col-sm-3 col-form-label o3-form-copy", + css_class="col-sm-9", + ), + ) + category = colander.SchemaNode( + colander.String(), + widget=deform.widget.SelectWidget( + values=CATEGORIES, + css_class="custom-select col-sm-9", + item_css_class="row", + label_css_class="col-sm-3 col-form-label", + ), + ) + catalog_nr = colander.SchemaNode( + colander.String(), + widget=deform.widget.TextInputWidget( + item_css_class="row", + label_css_class="col-sm-3 col-form-label o3-form-copy", + css_class="col-sm-9", + ), + ) + vendor = colander.SchemaNode( + colander.String(), + widget=deform.widget.TextInputWidget( + item_css_class="row", + label_css_class="col-sm-3 col-form-label o3-form-copy", + css_class="col-sm-9", + ), + ) + package_size = colander.SchemaNode( + colander.String(), + widget=deform.widget.TextInputWidget( + item_css_class="row", + label_css_class="col-sm-3 col-form-label o3-form-copy", + css_class="col-sm-9", + ), + ) + + +class MoneyInputSchema(colander.Schema): + """ custom schema for structured money and currency input """ + + amount = colander.SchemaNode( + colander.Decimal(), + widget=deform.widget.MoneyInputWidget( + readonly_template="textinput_disabled.pt", + css_class="moneyinput amount", + col_css_class="col-sm-7", + ), + ) + currency = colander.SchemaNode( + colander.String(), + default="EUR", + widget=deform.widget.TextInputWidget( + readonly_template="textinput_disabled.pt", + css_class="moneyinput currency", + col_css_class="col-sm-2", + ), + ) + + def __init__(self, *args, **kwargs): + """ define the custom schema templates """ + if "widget" not in kwargs: + readonly = kwargs.pop("readonly", False) + kwargs["widget"] = deform.widget.MappingWidget( + category="default", + template="money_mapping.pt", + readonly_template="money_mapping_disabled.pt", + item_template="money_mapping_item.pt", + item_readonly_template="money_mapping_item_diabled.pt", + readonly=readonly, + label_css_class="col-sm-3 col-form-label o3-form-copy", + item_css_class="row", + ) + super().__init__(*args, **kwargs) + + +class OrderPricing(colander.Schema): + """ schema for editing price information + + parital schema, used in NewOrderSchema and EditOrderSchema + """ + + quantity = colander.SchemaNode( + colander.Integer(), + validator=colander.Range(min=1), + widget=deform.widget.TextInputWidget( + css_class="number col-sm-9", + item_css_class="row", + label_css_class="col-sm-3 col-form-label o3-form-copy", + ), + default=1, + ) + unit_price = MoneyInputSchema( + readonly=False, label_css_class="col-sm-3 col-form-label o3-form-copy" + ) + total_price = MoneyInputSchema( + readonly=True, label_css_class="col-sm-3 col-form-label o3-form-copy" + ) + + +class OrderOptionals(colander.Schema): + """ schema for editing optional information + + parital schema, used in NewOrderSchema and EditOrderSchema + """ + + account = colander.SchemaNode( + colander.String(), + required=False, + missing="", + widget=deform.widget.TextInputWidget( + item_css_class="row", + label_css_class="col-sm-3 col-form-label o3-form-copy", + css_class="col-sm-9", + ), + ) + comment = colander.SchemaNode( + colander.String(), + missing="", + widget=deform.widget.TextAreaWidget( + rows=7, + item_css_class="row", + label_css_class="col-sm-3 col-form-label o3-form-copy", + css_class="col-sm-9", + ), + ) + + +class EditOrderSchema(CSRFSchema): + """ edit an order """ + + status = OrderStatus() + item = OrderItem() + pricing = OrderPricing() + optional = OrderOptionals() + + @classmethod + def as_form(cls, request, **override): + """ returns the schema as a form """ + settings = { + "buttons": ("Save Changes", "Cancel"), + "css_class": "deform o3-col-form", + } + settings.update(override) + form = super().as_form(request, **settings) + + # disable the status field, if the current user is not a purchaser + if UserRole.PURCHASER.principal not in request.user.principals: + form["status"]["status"].widget = deform.widget.TextInputWidget( + template="textinput_disabled.pt", + item_css_class="row", + label_css_class="col-sm-3 col-form-label", + css_class="col-sm-9", + ) + return form diff --git a/ordr3/static/script.js b/ordr3/static/script.js index 97201e6..30496ba 100644 --- a/ordr3/static/script.js +++ b/ordr3/static/script.js @@ -22,6 +22,15 @@ var load_more = function(target) { }); } +var update_price = function() { + // update total price in order form + var target = $(".item-unit_price .item-amount input"); + var price = target.val().replace(/,/g, "") + var amount = $(".item-quantity input").val() + var total = price * amount + $(".item-total_price .item-amount input").val(total.toFixed(2)) +} + $(function() { // Handler for .ready() called. @@ -131,6 +140,46 @@ $(function() { selects.val(target.attr("data-multi-value")) }); + $(".o3-back").on("click", function(event){ + // back button + history.back(); + return false; + }); + + $(".item-unit_price .item-amount input").on("keyup", function(event) { + update_price(); + }); + + $(".item-quantity input").on("keyup", function(event) { + update_price(); + }); + + $(".item-unit_price .item-currency input").on("keyup", function(event) { + // update total price currency in order form + var target = $(event.delegateTarget); + var value = target.val() + $(".item-total_price .item-currency input").val(value) + + }); + + $("label.o3-form-copy").on("click", function(event){ + // copy to clipboard + var target = $(event.delegateTarget); + var parent = target.parent() + var input = parent.find(".form-control") + var value = input.first().val() + var $temp = $(''); + $("body").append($temp); + $temp.val(value).select(); + document.execCommand("copy"); + $temp.remove(); + $(target).fadeTo(100, 0).delay(100).fadeTo(100, 1); + $(input).fadeTo(100, 0).delay(100).fadeTo(100, 1); + + }); + + $('label.o3-form-copy').attr('title', 'copy to clipboard'); + var infinite = new Waypoint.Infinite({ element: $('.infinite-container')[0] }); diff --git a/ordr3/static/style.css b/ordr3/static/style.css index 0aebd88..fe8dc02 100644 --- a/ordr3/static/style.css +++ b/ordr3/static/style.css @@ -193,3 +193,54 @@ td.o3-actions a:hover { .o3-data-edit-multiple select { width: 7rem; } + +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; + } + + +span.item-currency { + padding-left:0px; + padding-right:0px; + } + +input.moneyinput.amount, input.number { + text-align:right; + } + +input.moneyinput.currency { + text-align:center; + } + + +.panel-heading { + border-bottom:1px solid #ced4da!important; + margin-bottom:1rem; + margin-top:2rem; + color:#6c757d!important; + font-weight: lighter!important; + } + +label.o3-form-copy { + cursor:pointer; + } + +label.o3-form-copy:hover { + text-decoration:underline; +} diff --git a/ordr3/templates/account/login.jinja2 b/ordr3/templates/account/login.jinja2 index 14a7717..edffb47 100644 --- a/ordr3/templates/account/login.jinja2 +++ b/ordr3/templates/account/login.jinja2 @@ -13,7 +13,7 @@