Browse Source

added editing orders and placing custom orders

splash screen for new orders still missing
php2python
Holger Frey 7 years ago
parent
commit
b9e3be30ce
  1. 26
      ordr2/models/orders.py
  2. 39
      ordr2/schemas/orders.py
  3. 10
      ordr2/static/css/style.css
  4. 28
      ordr2/static/js/functions.js
  5. 71
      ordr2/templates/deform/order_info_mapping.pt
  6. 24
      ordr2/templates/orders/edit.jinja2
  7. 2
      ordr2/templates/orders/list.jinja2
  8. 24
      ordr2/templates/orders/new.jinja2
  9. 173
      ordr2/views/orders.py

26
ordr2/models/orders.py

@ -96,3 +96,29 @@ class Order(Base):
def __str__(self): def __str__(self):
''' string representation ''' ''' string representation '''
return '{!s} ({!s})'.format(self.cas_description, self.vendor) return '{!s} ({!s})'.format(self.cas_description, self.vendor)
def _date_info(self, some_date, some_one):
if not some_date:
return ''
if some_one:
return '{!s} by {!s}'.format(some_date, some_one)
else:
return '{!s}'.format(some_date)
@property
def placed(self):
return self._date_info(self.created_date, self.created_by)
@property
def approved(self):
return self._date_info(self.approval_date, self.approval_by)
@property
def ordered(self):
return self._date_info(self.ordered_date, self.ordered_by)
@property
def completed(self):
return self._date_info(self.completed_date, self.completed_by)

39
ordr2/schemas/orders.py

@ -66,33 +66,12 @@ class ConsumableSchema(CSRFSchema):
return super().as_form(request, **settings) return super().as_form(request, **settings)
class OrderInformation(colander.Schema): class OrderInformation(colander.Schema):
status = colander.SchemaNode( status = colander.SchemaNode(
colander.String(), colander.String(),
widget=deform.widget.SelectWidget(values=STATI) widget=deform.widget.SelectWidget(values=STATI)
) )
placed = colander.SchemaNode(
colander.String(),
widget=deform.widget.TextInputWidget(readonly=True),
missing=''
)
approval = colander.SchemaNode(
colander.String(),
widget=deform.widget.TextInputWidget(readonly=True),
missing=''
)
ordered = colander.SchemaNode(
colander.String(),
widget=deform.widget.TextInputWidget(readonly=True),
missing=''
)
completed = colander.SchemaNode(
colander.String(),
widget=deform.widget.TextInputWidget(readonly=True),
missing=''
)
class OrderItem(colander.Schema): class OrderItem(colander.Schema):
@ -123,6 +102,9 @@ class OrderPricing(colander.Schema):
quantity = colander.SchemaNode( quantity = colander.SchemaNode(
colander.Integer(), colander.Integer(),
validator=colander.Range(min=1), validator=colander.Range(min=1),
widget=deform.widget.TextInputWidget(
css_class='number'
),
default=1 default=1
) )
total_price = MoneyInputSchema( total_price = MoneyInputSchema(
@ -148,12 +130,15 @@ class NewOrderSchema(CSRFSchema):
item_information = OrderItem() item_information = OrderItem()
pricing = OrderPricing() pricing = OrderPricing()
optional_informatoin = OrderOptionals() optional_information = OrderOptionals()
@classmethod @classmethod
def as_form(cls, request, **override): def as_form(cls, request, **override):
settings = { settings = {
'buttons': ('Place Order', 'Cancel'), 'buttons': (
deform.Button(name='save', title='Place Order'),
deform.Button(name='cancel', title='Cancel')
),
'css_class': 'form-horizontal' 'css_class': 'form-horizontal'
} }
settings.update(override) settings.update(override)
@ -163,10 +148,14 @@ class NewOrderSchema(CSRFSchema):
class EditOrderSchema(CSRFSchema): class EditOrderSchema(CSRFSchema):
''' edit or add an order ''' ''' edit or add an order '''
order_information = OrderInformation() order_information = OrderInformation(
widget=deform.widget.MappingWidget(
template='order_info_mapping.pt'
)
)
item_information = OrderItem() item_information = OrderItem()
pricing = OrderPricing() pricing = OrderPricing()
optional_informatoin = OrderOptionals() optional_information = OrderOptionals()
@classmethod @classmethod
def as_form(cls, request, **override): def as_form(cls, request, **override):

10
ordr2/static/css/style.css

@ -725,15 +725,17 @@ input[value="password:mapping"] + div { margin-bottom:10px; }
input[value="new_password:mapping"] + div { margin-bottom:10px; } input[value="new_password:mapping"] + div { margin-bottom:10px; }
.form-horizontal.user-settings fieldset > .controls { margin-left:0; } .form-horizontal.user-settings fieldset > .controls { margin-left:0; }
.user-settings .panel-heading { .user-settings .panel-heading,
.edit-order .panel-heading {
font-size:150%; font-size:150%;
padding-top: 20px; padding-top: 20px;
padding-bottom: 20px; padding-bottom: 20px;
margin-bottom: 20px; margin-bottom: 20px;
border-bottom: 1px solid #aaa;} border-bottom: 1px solid #aaa;}
div.alert a { color:inherit; text-decoration:underline; } div.alert a { color:inherit; text-decoration:underline; }
td.column-pkg, td.column-price { text-align:right; } td.column-pkg, td.column-price, td.column-total, td.column-amount {
text-align:right;}
input.number { text-align:right; }
.moneyinput .amount { width:167px; text-align:right;} .moneyinput .amount { width:167px; text-align:right;}
.moneyinput .currency { width:30px; text-align:center;} .moneyinput .currency { width:30px; text-align:center;}
.controls .form-control-static { padding-top:5px; }

28
ordr2/static/js/functions.js

@ -89,29 +89,29 @@ $(document).ready(function() {
}); });
// calculator // calculator
if ( $('input[name="price_unit"]').length ) { if ( $('.item-unit_price input[name="amount"]').length ) {
if( $('input[name="price_unit"]').val() != '' && $('input[name="quantity"]').val() != '' ){ if( $('.item-unit_price input[name="amount"]').val() != '' && $('input[name="quantity"]').val() != '' ){
var total = $('input[name="price_unit"]').val().replace(",", ".") * $('input[name="quantity"]').val(); var total = $('.item-unit_price input[name="amount"]').val().replace(",", "") * $('input[name="quantity"]').val();
total = Math.round(total*100)/100; total = Math.round(total*100)/100;
$('input[name="price_total_disabled"]').attr( 'placeholder', total ); $('.item-total_price input[name="amount"]').attr( 'value', total );
} }
$('input[name="price_unit"], input[name="quantity"]').keyup(function() { $('.item-unit_price input[name="amount"], input[name="quantity"]').keyup(function() {
var total = $('input[name="price_unit"]').val().replace(",", ".") * $('input[name="quantity"]').val(); var total = $('.item-unit_price input[name="amount"]').val().replace(",", "") * $('input[name="quantity"]').val();
total = Math.round(total*100)/100; total = Math.round(total*100)/100;
$('input[name="price_total_disabled"]').attr( 'placeholder', total ); $('.item-total_price input[name="amount"]').attr( 'value', total );
}); });
} }
if ( $('input[name="currency"]').length ) { if ( $('.item-unit_price input[name="currency"]').length ) {
// added currency to total price // added currency to total price
if( $('input[name="currency"]').val() != '' ){ if( $('.item-unit_price input[name="currency"]').val() != '' ){
$('input[name="currency_disabled"]').attr( 'placeholder', $('input[name="currency"]').val() ); $('.item-total_price input[name="currency"]').attr( 'value', $('.item-unit_price input[name="currency"]').val() );
} }
$('input[name="currency"]').keyup(function() { $('.item-unit_price input[name="currency"]').keyup(function() {
$('input[name="currency_disabled"]').attr( 'placeholder', $('input[name="currency"]').val() ); $('.item-total_price input[name="currency"]').attr( 'value', $('.item-unit_price input[name="currency"]').val() );
}); });
$('input[name="currency"]').change(function() { $('.item-unit_price input[name="currency"]').change(function() {
$('input[name="currency_disabled"]').attr( 'placeholder', $('input[name="currency"]').val() ); $('.item-total_price input[name="currency"]').attr( 'value', $('.item-unit_price input[name="currency"]').val() );
}); });
} }

71
ordr2/templates/deform/order_info_mapping.pt

@ -0,0 +1,71 @@
<tal:def tal:define="title title|field.title;
description description|field.description;
errormsg errormsg|field.errormsg;
item_template item_template|field.widget.item_template;
request field.schema.bindings['request']"
i18n:domain="deform">
<div class="panel panel-default" title="${description}">
<div class="panel-heading">${title}</div>
<div class="panel-body">
<div tal:condition="errormsg"
class="clearfix alert alert-danger">
<p i18n:translate="">
There was a problem with this section
</p>
<p>${errormsg}</p>
</div>
<div tal:condition="description">
${description}
</div>
${field.start_mapping()}
<div tal:repeat="child field.children"
tal:replace="structure child.render_template(item_template)" >
</div>
<div class="control-group">
<label class="control-label"> Placed </label>
<div class="controls">
<p class="form-control-static">
${request.context.model.placed}
</p>
</div>
</div>
<div class="control-group">
<label class="control-label"> Approved </label>
<div class="controls">
<p class="form-control-static">
${request.context.model.approved}
</p>
</div>
</div>
<div class="control-group">
<label class="control-label"> Ordered </label>
<div class="controls">
<p class="form-control-static">
${request.context.model.ordered}
</p>
</div>
</div>
<div class="control-group">
<label class="control-label"> Completed </label>
<div class="controls">
<p class="form-control-static">
${request.context.model.completed}
</p>
</div>
</div>
${field.end_mapping()}
</div>
</div>
</tal:def>

24
ordr2/templates/orders/edit.jinja2

@ -0,0 +1,24 @@
{% extends "ordr2:templates/layout.jinja2" %}
{% import 'ordr2:templates/macros.jinja2' as macros with context %}
{% block subtitle %} Order | {{ context.model.cas_description }} {% endblock subtitle %}
{% block content %}
<div class="content controls">
<div class="container-fluid">
<div class="row-fluid">
<div class="page-controls">
<h1>Edit Order: {{ context.model.cas_description }}</h1>
</div>
</div>
<div class="row edit-order">
<div class="span8">
{{ macros.flash_messages() }}
{{form.render()|safe}}
</div>
</div>
</div>
</div>
{% endblock content %}

2
ordr2/templates/orders/order_list.jinja2 → ordr2/templates/orders/list.jinja2

@ -19,7 +19,7 @@
</div> </div>
<div class="span10"> <div class="span10">
<form action="{{ request.resource_url(context, 'actions') }}" method="POST"> <form action="{{ request.resource_url(context, 'actions', query=context.query_params()) }}" method="POST">
<input type="hidden" name="csrf_token" value="{{get_csrf_token()}}"> <input type="hidden" name="csrf_token" value="{{get_csrf_token()}}">
<div class="page-controls"> <div class="page-controls">
<div class="input-append search"> <div class="input-append search">

24
ordr2/templates/orders/new.jinja2

@ -0,0 +1,24 @@
{% extends "ordr2:templates/layout.jinja2" %}
{% import 'ordr2:templates/macros.jinja2' as macros with context %}
{% block subtitle %} Order | Create New Order {% endblock subtitle %}
{% block content %}
<div class="content controls">
<div class="container-fluid">
<div class="row-fluid">
<div class="page-controls">
<h1>Create New Order</h1>
</div>
</div>
<div class="row edit-order">
<div class="span8">
{{ macros.flash_messages() }}
{{form.render()|safe}}
</div>
</div>
</div>
</div>
{% endblock content %}

173
ordr2/views/orders.py

@ -1,12 +1,14 @@
import deform import deform
from datetime import datetime
from pyramid.httpexceptions import HTTPFound from pyramid.httpexceptions import HTTPFound
from pyramid.renderers import render from pyramid.renderers import render
from pyramid.view import view_config from pyramid.view import view_config
from ordr2.events import OrderStatusChange from ordr2.events import OrderStatusChange
from ordr2.models import Category, Order, OrderStatus, User from ordr2.models import Category, Order, OrderStatus, User
from ordr2.schemas.orders import ConsumableSchema from ordr2.schemas.orders import NewOrderSchema, EditOrderSchema
from . import update_column_display from . import update_column_display
@ -34,7 +36,7 @@ def change_in_order_status(request, order, old):
@view_config( @view_config(
context='ordr2:resources.OrderList', context='ordr2:resources.OrderList',
permission='view', permission='view',
renderer='ordr2:templates/orders/order_list.jinja2' renderer='ordr2:templates/orders/list.jinja2'
) )
def order_list(context, request): def order_list(context, request):
''' display the order list ''' ''' display the order list '''
@ -174,11 +176,178 @@ def order_delete_form(context, request):
return {'orders': [context.model]} return {'orders': [context.model]}
@view_config(
context='ordr2:resources.OrderResource',
name='edit',
permission='edit',
request_method='GET',
renderer='ordr2:templates/orders/edit.jinja2'
)
def order_edit_form(context, request):
form = EditOrderSchema.as_form(request)
order = context.model
info = {
'status': order.status.name
}
item = {
'cas_description': order.cas_description,
'category': order.category.name,
'vendor': order.vendor,
'catalog_nr': order.catalog_nr,
'package_size': order.package_size
}
pricing = {
'unit_price': {
'amount': '%.2f' % order.unit_price,
'currency': order.currency
},
'quantity': order.amount,
'total_price': {
'amount': '%.2f' % order.total_price,
'currency': order.currency
},
}
optional = {
'account': order.account,
'comment': order.comment
}
form_data = {
'order_information': info,
'item_information': item,
'pricing': pricing,
'optional_information': optional
}
form.set_appstruct(form_data)
return {'form': form}
@view_config(
context='ordr2:resources.OrderResource',
name='edit',
permission='edit',
request_method='POST',
renderer='ordr2:templates/orders/edit.jinja2'
)
def order_edit_form_processing(context, request):
''' process the consumable edit form '''
form = EditOrderSchema.as_form(request)
data = request.POST.items()
if 'save' in request.POST:
try:
appstruct = form.validate(data)
except deform.ValidationFailure as e:
return {'form': form}
# form validation sucessful, change order
order = context.model
info = appstruct['order_information']
item = appstruct['item_information']
pricing = appstruct['pricing']
optional = appstruct['optional_information']
old_status = order.status
order.status = OrderStatus[info['status']]
order.cas_description = item['cas_description']
order.category = item['category']
order.vendor = item['vendor']
order.catalog_nr = item['catalog_nr']
order.package_size = item['package_size']
order.unit_price = pricing['unit_price']['amount']
order.currency = pricing['unit_price']['currency']
order.amount = pricing['quantity']
order.total_price = order.unit_price * order.amount
order.account = optional['account']
order.comment = optional['comment']
if old_status != order.status:
change_in_order_status(request, order, old_status)
if order.status == OrderStatus.APPROVAL:
order.approval_date = datetime.utcnow()
order.approval_by = request.user.user_name
if order.status == OrderStatus.ORDERED:
order.ordered_date = datetime.utcnow()
order.ordered_by = request.user.user_name
if order.status == OrderStatus.COMPLETED:
order.completed_date = datetime.utcnow()
order.completed_by = request.user.user_name
msg = 'Order <em>{!s}</em> updated.'.format(context.model)
request.flash('success', msg)
elif 'delete' in request.POST and context.model:
return HTTPFound(request.resource_url(context, 'delete'))
return HTTPFound(context.__parent__.url())
@view_config(
context='ordr2:resources.OrderList',
name='new',
permission='create',
request_method='GET',
renderer='ordr2:templates/orders/new.jinja2'
)
def order_new_form(context, request):
form = NewOrderSchema.as_form(request)
return {'form': form}
@view_config(
context='ordr2:resources.OrderList',
name='new',
permission='create',
request_method='POST',
renderer='ordr2:templates/orders/new.jinja2'
)
def order_new_form_processing(context, request):
''' process the consumable edit form '''
form = NewOrderSchema.as_form(request)
data = request.POST.items()
if 'save' in request.POST:
try:
appstruct = form.validate(data)
except deform.ValidationFailure as e:
return {'form': form}
# form validation sucessful, change order
order = Order(
status=OrderStatus.OPEN,
created_by=request.user.user_name
)
item = appstruct['item_information']
pricing = appstruct['pricing']
optional = appstruct['optional_information']
order.cas_description = item['cas_description']
order.category = item['category']
order.vendor = item['vendor']
order.catalog_nr = item['catalog_nr']
order.package_size = item['package_size']
order.unit_price = pricing['unit_price']['amount']
order.currency = pricing['unit_price']['currency']
order.amount = pricing['quantity']
order.total_price = order.unit_price * order.amount
order.account = optional['account']
order.comment = optional['comment']
request.dbsession.add(order)
msg = 'Order <em>{!s}</em> created.'.format(context.model)
request.flash('success', msg)
return HTTPFound(context.url())