
33 changed files with 665 additions and 88 deletions
@ -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 |
@ -0,0 +1,38 @@ |
|||||||
|
<span tal:define="error_class error_class|field.widget.error_class; |
||||||
|
description description|field.description; |
||||||
|
title title|field.title; |
||||||
|
hidden hidden|field.widget.hidden; |
||||||
|
category category|field.widget.category; |
||||||
|
item_template item_template|field.widget.item_template; |
||||||
|
amount_field field.children[0]; |
||||||
|
currency_field field.children[1]; |
||||||
|
oid oid|amount_field.oid; |
||||||
|
required required|amount_field.required; |
||||||
|
label_css_class amount_field.widget.label_css_class|'';" |
||||||
|
i18n:domain="deform" |
||||||
|
class="moneyinput" |
||||||
|
tal:omit-tag> |
||||||
|
|
||||||
|
${field.start_mapping()} |
||||||
|
<div tal:repeat="child field.children" |
||||||
|
tal:replace="structure child.render_template(item_template)" > |
||||||
|
</div> |
||||||
|
${field.end_mapping()} |
||||||
|
|
||||||
|
<p class="help-inline" |
||||||
|
tal:define="errstr 'error-%s' % field.oid" |
||||||
|
tal:repeat="msg amount_field.error.messages()|currency_field.error.messages()" |
||||||
|
i18n:translate="" |
||||||
|
tal:attributes="id repeat.msg.index==0 and errstr or |
||||||
|
('%s-%s' % (errstr, repeat.msg.index))" |
||||||
|
tal:condition="(amount_field.error or currency_field.error) and not field.widget.hidden"> |
||||||
|
${msg} |
||||||
|
</p> |
||||||
|
|
||||||
|
<p tal:condition="field.description and not field.widget.hidden" |
||||||
|
class="help-inline" > |
||||||
|
${field.description} |
||||||
|
</p> |
||||||
|
|
||||||
|
</span> |
||||||
|
|
@ -0,0 +1,38 @@ |
|||||||
|
<span tal:define="error_class error_class|field.widget.error_class; |
||||||
|
description description|field.description; |
||||||
|
title title|field.title; |
||||||
|
hidden hidden|field.widget.hidden; |
||||||
|
category category|field.widget.category; |
||||||
|
item_template item_template|field.widget.item_readonly_template; |
||||||
|
amount_field field.children[0]; |
||||||
|
currency_field field.children[1]; |
||||||
|
oid oid|amount_field.oid; |
||||||
|
required required|amount_field.required; |
||||||
|
label_css_class amount_field.widget.label_css_class|'';" |
||||||
|
i18n:domain="deform" |
||||||
|
class="moneyinput" |
||||||
|
tal:omit-tag> |
||||||
|
|
||||||
|
${field.start_mapping()} |
||||||
|
<div tal:repeat="child field.children" |
||||||
|
tal:replace="structure child.render_template(item_template)" > |
||||||
|
</div> |
||||||
|
${field.end_mapping()} |
||||||
|
|
||||||
|
<p class="help-inline" |
||||||
|
tal:define="errstr 'error-%s' % field.oid" |
||||||
|
tal:repeat="msg amount_field.error.messages()|currency_field.error.messages()" |
||||||
|
i18n:translate="" |
||||||
|
tal:attributes="id repeat.msg.index==0 and errstr or |
||||||
|
('%s-%s' % (errstr, repeat.msg.index))" |
||||||
|
tal:condition="(amount_field.error or currency_field.error) and not field.widget.hidden"> |
||||||
|
${msg} |
||||||
|
</p> |
||||||
|
|
||||||
|
<p tal:condition="field.description and not field.widget.hidden" |
||||||
|
class="help-inline" > |
||||||
|
${field.description} |
||||||
|
</p> |
||||||
|
|
||||||
|
</span> |
||||||
|
|
@ -0,0 +1,26 @@ |
|||||||
|
<span tal:define="error_class error_class|field.widget.error_class; |
||||||
|
description description|field.description; |
||||||
|
title title|field.title; |
||||||
|
oid oid|field.oid; |
||||||
|
hidden hidden|field.widget.hidden; |
||||||
|
category category|field.widget.category; |
||||||
|
structural hidden or category == 'structural'; |
||||||
|
required required|field.required; |
||||||
|
label_css_class label_css_class|field.widget.label_css_class|'';" |
||||||
|
class="${field.error and 'error' or ''} ${field.widget.col_css_class or ''} ${field.default_item_css_class()}" |
||||||
|
title="${description}" |
||||||
|
id="item-${oid}" |
||||||
|
tal:omit-tag="structural" |
||||||
|
i18n:domain="deform"> |
||||||
|
|
||||||
|
<div tal:define="input_prepend field.widget.input_prepend | None; |
||||||
|
input_append field.widget.input_append | None" |
||||||
|
tal:omit-tag="not (input_prepend or input_append)" |
||||||
|
class="input-group"> |
||||||
|
<span class="input-group-addon" |
||||||
|
tal:condition="input_prepend">${input_prepend}</span |
||||||
|
><span tal:replace="structure field.serialize(cstruct).strip()" |
||||||
|
/><span class="input-group-addon" |
||||||
|
tal:condition="input_append">${input_append}</span> |
||||||
|
</div> |
||||||
|
</span> |
@ -0,0 +1,26 @@ |
|||||||
|
<span tal:define="error_class error_class|field.widget.error_class; |
||||||
|
description description|field.description; |
||||||
|
title title|field.title; |
||||||
|
oid oid|field.oid; |
||||||
|
hidden hidden|field.widget.hidden; |
||||||
|
category category|field.widget.category; |
||||||
|
structural hidden or category == 'structural'; |
||||||
|
required required|field.required; |
||||||
|
label_css_class label_css_class|field.widget.label_css_class|'';" |
||||||
|
class="${field.error and 'error' or ''} ${field.widget.col_css_class or ''} ${field.default_item_css_class()}" |
||||||
|
title="${description}" |
||||||
|
id="item-${oid}" |
||||||
|
tal:omit-tag="structural" |
||||||
|
i18n:domain="deform"> |
||||||
|
|
||||||
|
<div tal:define="input_prepend field.widget.input_prepend | None; |
||||||
|
input_append field.widget.input_append | None" |
||||||
|
tal:omit-tag="not (input_prepend or input_append)" |
||||||
|
class="input-group"> |
||||||
|
<span class="input-group-addon" |
||||||
|
tal:condition="input_prepend">${input_prepend}</span |
||||||
|
><span tal:replace="structure field.serialize(cstruct, readonly=True).strip()" |
||||||
|
/><span class="input-group-addon" |
||||||
|
tal:condition="input_append">${input_append}</span> |
||||||
|
</div> |
||||||
|
</span> |
@ -0,0 +1,28 @@ |
|||||||
|
{% extends "ordr3:templates/layout_full.jinja2" %} |
||||||
|
|
||||||
|
{% block subtitle %} Edit Order {{ context.model.cas_description }} {% endblock subtitle %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
|
||||||
|
<div class="col-5"> |
||||||
|
<h4 class="mb-2 text-muted mb-4 text-truncate">Edit Order <span class="text-dark">{{ context.model.cas_description }}</span></h4> |
||||||
|
{{form.render()|safe}} |
||||||
|
<hr> |
||||||
|
<p class="mt-4"> |
||||||
|
{% if request.has_permission("add", context.__parent__) %} |
||||||
|
<a href="{{ context|resource_url('reorder') }}" class="btn btn-outline-primary">Reorder Item</a> |
||||||
|
{% endif %} |
||||||
|
{% if request.has_permission("delete", context) %} |
||||||
|
<a href="{{ context|resource_url('delete') }}" class="btn btn-outline-danger">Delete Order</a> |
||||||
|
{% endif %} |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="col-5"> |
||||||
|
<div class="h4"> </div> |
||||||
|
{{ macros.change_log(context.model) }} |
||||||
|
</div> |
||||||
|
|
||||||
|
{% endblock content %} |
||||||
|
|
||||||
|
|
Loading…
Reference in new issue