Add support for 'receiving' mode for purchase batches

This commit is contained in:
Lance Edgar 2016-11-21 01:07:35 -06:00
parent 8acb9b0029
commit 67f6c11307
9 changed files with 292 additions and 70 deletions

View file

@ -49,6 +49,12 @@ class ProductFieldRenderer(AutocompleteFieldRenderer):
return product.full_description return product.full_description
return '' return ''
def render_readonly(self, **kwargs):
product = self.raw_value
if not product:
return ''
return tags.link_to(product, self.request.route_url('products.view', uuid=product.uuid))
class GPCFieldRenderer(TextFieldRenderer): class GPCFieldRenderer(TextFieldRenderer):
""" """

View file

@ -26,7 +26,7 @@ Vendor Field Renderers
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
import formalchemy as fa from formalchemy.fields import SelectFieldRenderer
from webhelpers.html import tags from webhelpers.html import tags
from tailbone.forms.renderers.common import AutocompleteFieldRenderer from tailbone.forms.renderers.common import AutocompleteFieldRenderer
@ -45,7 +45,7 @@ class VendorFieldRenderer(AutocompleteFieldRenderer):
return tags.link_to(vendor, self.request.route_url('vendors.view', uuid=vendor.uuid)) return tags.link_to(vendor, self.request.route_url('vendors.view', uuid=vendor.uuid))
class PurchaseFieldRenderer(fa.FieldRenderer): class PurchaseFieldRenderer(SelectFieldRenderer):
""" """
Renderer for :class:`rattail.db.model.Purchase` relation fields. Renderer for :class:`rattail.db.model.Purchase` relation fields.
""" """

View file

@ -27,7 +27,6 @@ Event Subscribers
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
import rattail import rattail
from rattail import enum
from rattail.db import model from rattail.db import model
from rattail.db.auth import has_permission, administrator_role from rattail.db.auth import has_permission, administrator_role
@ -79,6 +78,7 @@ def before_render(event):
renderer_globals['url'] = request.route_url renderer_globals['url'] = request.route_url
renderer_globals['rattail'] = rattail renderer_globals['rattail'] = rattail
renderer_globals['tailbone'] = tailbone renderer_globals['tailbone'] = tailbone
renderer_globals['enum'] = request.rattail_config.get_enum()
def add_inbox_count(event): def add_inbox_count(event):
@ -92,6 +92,7 @@ def add_inbox_count(event):
request = event.get('request') or threadlocal.get_current_request() request = event.get('request') or threadlocal.get_current_request()
if request.user: if request.user:
renderer_globals = event renderer_globals = event
enum = request.rattail_config.get_enum()
renderer_globals['inbox_count'] = Session.query(model.Message)\ renderer_globals['inbox_count'] = Session.query(model.Message)\
.outerjoin(model.MessageRecipient)\ .outerjoin(model.MessageRecipient)\
.filter(model.MessageRecipient.recipient == Session.merge(request.user))\ .filter(model.MessageRecipient.recipient == Session.merge(request.user))\

View file

@ -4,11 +4,71 @@
<%def name="head_tags()"> <%def name="head_tags()">
${parent.head_tags()} ${parent.head_tags()}
<script type="text/javascript"> <script type="text/javascript">
function show_mode(mode) {
if (mode == ${enum.PURCHASE_BATCH_MODE_NEW}) {
$('.field-wrapper.store_uuid').show();
$('.field-wrapper.purchase_uuid').hide();
$('.field-wrapper.buyer_uuid').show();
$('.field-wrapper.date_ordered').show();
$('.field-wrapper.date_received').hide();
} else if (mode == ${enum.PURCHASE_BATCH_MODE_RECEIVING}) {
$('.field-wrapper.store_uuid').hide();
$('.field-wrapper.purchase_uuid').show();
$('.field-wrapper.buyer_uuid').hide();
$('.field-wrapper.date_ordered').hide();
$('.field-wrapper.date_received').show();
}
}
function vendor_selected(uuid, name) {
var mode = $('.mode select').val();
if (mode == ${enum.PURCHASE_BATCH_MODE_RECEIVING}) {
var purchases = $('.purchase_uuid select');
purchases.empty();
var data = {'vendor_uuid': uuid, 'mode': mode};
$.get('${url('purchases.batch.eligible_purchases')}', data, function(data) {
if (data.error) {
alert(data.error);
} else {
$.each(data.purchases, function(i, purchase) {
purchases.append($('<option value="' + purchase.uuid + '">' + purchase.display + '</option>'));
});
}
});
// TODO: apparently refresh doesn't work right?
// http://stackoverflow.com/a/10280078
// purchases.selectmenu('refresh');
purchases.selectmenu('destroy').selectmenu();
}
}
function vendor_cleared() {
var purchases = $('.purchase_uuid select');
purchases.empty();
// TODO: apparently refresh doesn't work right?
// http://stackoverflow.com/a/10280078
// purchases.selectmenu('refresh');
purchases.selectmenu('destroy').selectmenu();
}
$(function() { $(function() {
$('.field-wrapper.mode select').selectmenu(); $('.field-wrapper.mode select').selectmenu({
change: function(event, ui) {
show_mode(ui.item.value);
}
});
$('.field-wrapper.purchase_uuid select').selectmenu();
show_mode(${form.fieldset.model.mode or enum.PURCHASE_BATCH_MODE_NEW});
}); });
</script> </script>
</%def> </%def>

View file

@ -154,7 +154,18 @@
<th>Pref.</th> <th>Pref.</th>
<th>Unit Cost</th> <th>Unit Cost</th>
% for data in history.itervalues(): % for data in history.itervalues():
<th>${data['purchase'].date_ordered.strftime('%m/%d') if data else ''}</th> <th>
% if data:
% if data['purchase'].date_ordered:
${data['purchase'].date_ordered.strftime('%m/%d') if data else ''}
% elif data['purchase'].date_received:
Rec.<br />
${data['purchase'].date_received.strftime('%m/%d') if data else ''}
% else:
??
% endif
% endif
</th>
% endfor % endfor
<th> <th>
${batch.date_ordered.strftime('%m/%d')}<br /> ${batch.date_ordered.strftime('%m/%d')}<br />
@ -180,9 +191,15 @@
<td class="unit-cost">$${'{:0.2f}'.format(cost.unit_cost)}</td> <td class="unit-cost">$${'{:0.2f}'.format(cost.unit_cost)}</td>
% for data in history.itervalues(): % for data in history.itervalues():
<td class="scratch_pad"> <td class="scratch_pad">
% if data and cost.product_uuid in data['items']: % if data:
${'{} / {}'.format(int(data['items'][cost.product_uuid].cases_ordered or 0), int(data['items'][cost.product_uuid].units_ordered or 0))} <% item = data['items'].get(cost.product_uuid) %>
## ${int(data['items'][cost.product_uuid].cases_ordered or 0) or ''} % if item:
% if data['purchase'].date_ordered and (item.cases_ordered is not None or item.units_ordered is not None):
${'{} / {}'.format(int(item.cases_ordered or 0), int(item.units_ordered or 0))}
% elif item.cases_received is not None or item.units_received is not None:
${'{} / {}'.format(int(item.cases_received or 0), int(item.units_received or 0))}
% endif
% endif
% endif % endif
</td> </td>
% endfor % endfor

View file

@ -23,7 +23,7 @@
</%def> </%def>
<%def name="leading_buttons()"> <%def name="leading_buttons()">
% if not batch.complete and not batch.executed and request.has_perm('purchases.batch.order_form'): % if batch.mode == enum.PURCHASE_BATCH_MODE_NEW and not batch.complete and not batch.executed and request.has_perm('purchases.batch.order_form'):
<button type="button" id="order-form">View as Order Form</button> <button type="button" id="order-form">View as Order Form</button>
% endif % endif
</%def> </%def>

View file

@ -237,19 +237,18 @@ class BatchMasterView(MasterView):
def save_create_form(self, form): def save_create_form(self, form):
self.before_create(form) self.before_create(form)
# current user is batch creator with Session.no_autoflush:
creator = self.request.user or self.late_login_user()
# transfer form data to batch instance # transfer form data to batch instance
form.fieldset.sync() form.fieldset.sync()
batch = form.fieldset.model batch = form.fieldset.model
batch.created_by = creator
# current user is batch creator
batch.created_by = self.request.user or self.late_login_user()
# destroy initial batch and re-make using handler # destroy initial batch and re-make using handler
kwargs = self.get_batch_kwargs(batch) kwargs = self.get_batch_kwargs(batch)
Session.expunge(batch) Session.expunge(batch)
# TODO: is no_autoflush necessary?
with Session.no_autoflush:
batch = self.handler.make_batch(Session(), **kwargs) batch = self.handler.make_batch(Session(), **kwargs)
Session.flush() Session.flush()
@ -279,6 +278,7 @@ class BatchMasterView(MasterView):
kwargs['filename'] = batch.filename kwargs['filename'] = batch.filename
return kwargs return kwargs
# TODO: deprecate / remove this (is it used at all now?)
def init_batch(self, batch): def init_batch(self, batch):
""" """
Initialize a new batch. Derived classes can override this to Initialize a new batch. Derived classes can override this to
@ -667,7 +667,7 @@ class BatchMasterView(MasterView):
fs.status_text.set(readonly=True) fs.status_text.set(readonly=True)
fs.removed.set(readonly=True) fs.removed.set(readonly=True)
try: try:
fs.product.set(readonly=True) fs.product.set(readonly=True, renderer=forms.renderers.ProductFieldRenderer)
except AttributeError: except AttributeError:
pass pass

View file

@ -36,6 +36,7 @@ from rattail.core import Object
from rattail.util import OrderedDict from rattail.util import OrderedDict
import formalchemy as fa import formalchemy as fa
from pyramid import httpexceptions
from tailbone import forms from tailbone import forms
from tailbone.db import Session from tailbone.db import Session
@ -56,6 +57,9 @@ class PurchaseBatchView(BatchMasterView):
rows_editable = True rows_editable = True
edit_with_rows = False edit_with_rows = False
def get_instance_title(self, batch):
return '{} ({})'.format(batch.id_str, self.enum.PURCHASE_BATCH_MODE[batch.mode])
def _preconfigure_grid(self, g): def _preconfigure_grid(self, g):
super(PurchaseBatchView, self)._preconfigure_grid(g) super(PurchaseBatchView, self)._preconfigure_grid(g)
@ -74,6 +78,7 @@ class PurchaseBatchView(BatchMasterView):
g.filters['complete'].default_verb = 'is_true' g.filters['complete'].default_verb = 'is_true'
g.date_ordered.set(label="Ordered") g.date_ordered.set(label="Ordered")
g.date_received.set(label="Received")
g.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.PURCHASE_BATCH_MODE)) g.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.PURCHASE_BATCH_MODE))
def configure_grid(self, g): def configure_grid(self, g):
@ -93,11 +98,14 @@ class PurchaseBatchView(BatchMasterView):
def _preconfigure_fieldset(self, fs): def _preconfigure_fieldset(self, fs):
super(PurchaseBatchView, self)._preconfigure_fieldset(fs) super(PurchaseBatchView, self)._preconfigure_fieldset(fs)
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.PURCHASE_BATCH_MODE)) fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.PURCHASE_BATCH_MODE))
fs.purchase.set(renderer=forms.renderers.PurchaseFieldRenderer) fs.purchase.set(renderer=forms.renderers.PurchaseFieldRenderer, options=[])
fs.vendor.set(renderer=forms.renderers.VendorFieldRenderer) fs.vendor.set(renderer=forms.renderers.VendorFieldRenderer,
attrs={'selected': 'vendor_selected',
'cleared': 'vendor_cleared'})
fs.buyer.set(renderer=forms.renderers.EmployeeFieldRenderer) fs.buyer.set(renderer=forms.renderers.EmployeeFieldRenderer)
fs.po_number.set(label="PO Number") fs.po_number.set(label="PO Number")
fs.po_total.set(label="PO Total", readonly=True) fs.po_total.set(label="PO Total", readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
fs.invoice_total.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
fs.append(fa.Field('vendor_email', readonly=True, fs.append(fa.Field('vendor_email', readonly=True,
value=lambda b: b.vendor.email.address if b.vendor.email else None)) value=lambda b: b.vendor.email.address if b.vendor.email else None))
@ -123,17 +131,19 @@ class PurchaseBatchView(BatchMasterView):
include=[ include=[
fs.id, fs.id,
fs.mode, fs.mode,
fs.purchase,
fs.store, fs.store,
fs.vendor, fs.vendor,
fs.purchase,
fs.vendor_email, fs.vendor_email,
fs.vendor_fax, fs.vendor_fax,
fs.vendor_contact, fs.vendor_contact,
fs.vendor_phone, fs.vendor_phone,
fs.buyer, fs.buyer,
fs.date_ordered, fs.date_ordered,
fs.date_received,
fs.po_number, fs.po_number,
fs.po_total, fs.po_total,
fs.invoice_total,
fs.created, fs.created,
fs.created_by, fs.created_by,
fs.complete, fs.complete,
@ -142,8 +152,8 @@ class PurchaseBatchView(BatchMasterView):
]) ])
if self.creating: if self.creating:
del fs.purchase
del fs.po_total del fs.po_total
del fs.invoice_total
del fs.complete del fs.complete
del fs.vendor_email del fs.vendor_email
del fs.vendor_fax del fs.vendor_fax
@ -163,12 +173,14 @@ class PurchaseBatchView(BatchMasterView):
if buyer: if buyer:
fs.model.buyer = buyer fs.model.buyer = buyer
# default order date is today # TODO: something tells me this isn't quite safe..
fs.model.date_ordered = localtime(self.rattail_config).date() # all dates have today as default
today = localtime(self.rattail_config).date()
fs.model.date_ordered = today
fs.model.date_received = today
# TODO: temp hack until we support more modes # TODO: temp hack until we support more modes
modes = dict(self.enum.PURCHASE_BATCH_MODE) modes = dict(self.enum.PURCHASE_BATCH_MODE)
del modes[self.enum.PURCHASE_BATCH_MODE_RECEIVING]
del modes[self.enum.PURCHASE_BATCH_MODE_COSTING] del modes[self.enum.PURCHASE_BATCH_MODE_COSTING]
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(modes)) fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(modes))
@ -176,6 +188,30 @@ class PurchaseBatchView(BatchMasterView):
fs.mode.set(readonly=True) fs.mode.set(readonly=True)
fs.store.set(readonly=True) fs.store.set(readonly=True)
fs.vendor.set(readonly=True) fs.vendor.set(readonly=True)
fs.purchase.set(readonly=True)
def eligible_purchases(self):
uuid = self.request.GET.get('vendor_uuid')
vendor = Session.query(model.Vendor).get(uuid) if uuid else None
if not vendor:
return {'error': "Must specify a vendor."}
mode = self.request.GET.get('mode')
mode = int(mode) if mode and mode.isdigit() else None
if not mode or mode not in self.enum.PURCHASE_BATCH_MODE:
return {'error': "Unknown mode: {}".format(mode)}
purchases = Session.query(model.Purchase)\
.filter(model.Purchase.vendor == vendor)
if mode == enum.PURCHASE_BATCH_MODE_RECEIVING:
purchases = purchases.filter(model.Purchase.status == self.enum.PURCHASE_STATUS_ORDERED)\
.order_by(model.Purchase.date_ordered, model.Purchase.created)
return {'purchases': [{'uuid': p.uuid, 'display': self.render_eligible_purchase(p)}
for p in purchases]}
def render_eligible_purchase(self, purchase):
return '{} for ${:0,.2f} ({})'.format(purchase.date_ordered, purchase.po_total, purchase.buyer)
def get_batch_kwargs(self, batch): def get_batch_kwargs(self, batch):
kwargs = super(PurchaseBatchView, self).get_batch_kwargs(batch) kwargs = super(PurchaseBatchView, self).get_batch_kwargs(batch)
@ -192,8 +228,20 @@ class PurchaseBatchView(BatchMasterView):
kwargs['buyer'] = batch.buyer kwargs['buyer'] = batch.buyer
elif batch.buyer_uuid: elif batch.buyer_uuid:
kwargs['buyer_uuid'] = batch.buyer_uuid kwargs['buyer_uuid'] = batch.buyer_uuid
kwargs['date_ordered'] = batch.date_ordered
kwargs['po_number'] = batch.po_number kwargs['po_number'] = batch.po_number
if batch.mode == self.enum.PURCHASE_BATCH_MODE_NEW:
kwargs['date_ordered'] = batch.date_ordered
elif batch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING:
kwargs['date_received'] = batch.date_received
if batch.purchase_uuid:
purchase = Session.query(model.Purchase).get(batch.purchase_uuid)
assert purchase
kwargs['purchase'] = purchase
kwargs['date_ordered'] = purchase.date_ordered
kwargs['po_total'] = purchase.po_total
return kwargs return kwargs
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
@ -214,11 +262,16 @@ class PurchaseBatchView(BatchMasterView):
g.upc.set(label="UPC") g.upc.set(label="UPC")
g.brand_name.set(label="Brand") g.brand_name.set(label="Brand")
g.cases_ordered.set(label="Cases") g.cases_ordered.set(label="Cases Ord.")
g.units_ordered.set(label="Units") g.units_ordered.set(label="Units Ord.")
g.po_total.set(label="Total") g.cases_received.set(label="Cases Rec.")
g.units_received.set(label="Units Rec.")
g.po_total.set(label="Total", renderer=forms.renderers.CurrencyFieldRenderer)
g.invoice_total.set(label="Total", renderer=forms.renderers.CurrencyFieldRenderer)
def configure_row_grid(self, g): def configure_row_grid(self, g):
batch = self.get_instance()
g.configure( g.configure(
include=[ include=[
g.sequence, g.sequence,
@ -228,32 +281,38 @@ class PurchaseBatchView(BatchMasterView):
g.size, g.size,
g.cases_ordered, g.cases_ordered,
g.units_ordered, g.units_ordered,
g.cases_received,
g.units_received,
g.po_total, g.po_total,
g.invoice_total,
g.status_code, g.status_code,
], ],
readonly=True) readonly=True)
if batch.mode == self.enum.PURCHASE_BATCH_MODE_NEW:
del g.cases_received
del g.units_received
del g.invoice_total
elif batch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING:
del g.po_total
def make_row_grid_tools(self, batch): def make_row_grid_tools(self, batch):
return self.make_default_row_grid_tools(batch) return self.make_default_row_grid_tools(batch)
# def row_grid_row_attrs(self, row, i): def row_grid_row_attrs(self, row, i):
# attrs = {} attrs = {}
# if row.status_code in (row.STATUS_NOT_IN_PURCHASE, if row.status_code in (row.STATUS_INCOMPLETE,
# row.STATUS_NOT_IN_INVOICE, row.STATUS_ORDERED_RECEIVED_DIFFER):
# row.STATUS_DIFFERS_FROM_PURCHASE): attrs['class_'] = 'notice'
# attrs['class_'] = 'notice' return attrs
# if row.status_code in (row.STATUS_NOT_IN_DB,
# row.STATUS_COST_NOT_IN_DB,
# row.STATUS_NO_CASE_QUANTITY):
# attrs['class_'] = 'warning'
# return attrs
def _preconfigure_row_fieldset(self, fs): def _preconfigure_row_fieldset(self, fs):
super(PurchaseBatchView, self)._preconfigure_row_fieldset(fs) super(PurchaseBatchView, self)._preconfigure_row_fieldset(fs)
fs.upc.set(label="UPC") fs.upc.set(label="UPC")
fs.brand_name.set(label="Brand") fs.brand_name.set(label="Brand")
fs.po_unit_cost.set(label="PO Unit Cost") fs.po_unit_cost.set(label="PO Unit Cost")
fs.po_total.set(label="PO Total") fs.po_total.set(label="PO Total", renderer=forms.renderers.CurrencyFieldRenderer)
fs.invoice_total.set(renderer=forms.renderers.CurrencyFieldRenderer)
fs.append(fa.Field('item_lookup', label="Item Lookup Code", required=True, fs.append(fa.Field('item_lookup', label="Item Lookup Code", required=True,
validate=self.item_lookup)) validate=self.item_lookup))
@ -277,35 +336,67 @@ class PurchaseBatchView(BatchMasterView):
raise fa.ValidationError("Product not found") raise fa.ValidationError("Product not found")
def configure_row_fieldset(self, fs): def configure_row_fieldset(self, fs):
try:
batch = self.get_instance()
except httpexceptions.HTTPNotFound:
batch = self.get_row_instance().batch
if self.creating:
fs.configure( fs.configure(
include=[ include=[
fs.item_lookup, fs.item_lookup,
fs.upc,
fs.product,
fs.cases_ordered, fs.cases_ordered,
fs.units_ordered, fs.units_ordered,
fs.cases_received,
fs.units_received,
fs.po_total,
fs.invoice_total,
]) ])
if self.creating:
del fs.upc
del fs.product
del fs.po_total
del fs.invoice_total
if batch.mode == self.enum.PURCHASE_BATCH_MODE_NEW:
del fs.cases_received
del fs.units_received
elif batch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING:
del fs.cases_ordered
del fs.units_ordered
elif self.editing: elif self.editing:
fs.configure( del fs.item_lookup
include=[ fs.upc.set(readonly=True)
fs.upc.readonly(), fs.product.set(readonly=True)
fs.product.readonly(), del fs.po_total
fs.cases_ordered, del fs.invoice_total
fs.units_ordered,
]) elif self.viewing:
del fs.item_lookup
def before_create_row(self, form): def before_create_row(self, form):
row = form.fieldset.model row = form.fieldset.model
batch = self.get_instance() batch = self.get_instance()
row.sequence = max([0] + [r.sequence for r in batch.data_rows]) + 1 batch.add_row(row)
row.batch = batch
# TODO: this seems heavy-handed but works.. # TODO: this seems heavy-handed but works..
row.product_uuid = self.item_lookup(form.fieldset.item_lookup.value) row.product_uuid = self.item_lookup(form.fieldset.item_lookup.value)
def after_create_row(self, row): def after_create_row(self, row):
self.handler.refresh_row(row) self.handler.refresh_row(row)
def after_edit_row(self, row):
batch = row.batch
# first undo any totals previously in effect for the row
if batch.mode == self.enum.PURCHASE_BATCH_MODE_NEW and row.po_total:
batch.po_total -= row.po_total
elif batch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING and row.invoice_total:
batch.invoice_total -= row.invoice_total
self.handler.refresh_row(row)
def redirect_after_create_row(self, row): def redirect_after_create_row(self, row):
self.request.session.flash("Added item: {} {}".format(row.upc.pretty(), row.product)) self.request.session.flash("Added item: {} {}".format(row.upc.pretty(), row.product))
return self.redirect(self.request.current_route_url()) return self.redirect(self.request.current_route_url())
@ -478,6 +569,12 @@ class PurchaseBatchView(BatchMasterView):
model_key = cls.get_model_key() model_key = cls.get_model_key()
model_title = cls.get_model_title() model_title = cls.get_model_title()
# eligible purchases (AJAX)
config.add_route('{}.eligible_purchases'.format(route_prefix), '{}/eligible-purchases'.format(url_prefix))
config.add_view(cls, attr='eligible_purchases', route_name='{}.eligible_purchases'.format(route_prefix),
renderer='json', permission='{}.view'.format(permission_prefix))
# defaults
cls._batch_defaults(config) cls._batch_defaults(config)
cls._defaults(config) cls._defaults(config)

View file

@ -45,8 +45,9 @@ class BatchesFieldRenderer(fa.FieldRenderer):
return '' return ''
def render(batch): def render(batch):
return tags.link_to('{} ({})'.format(batch.id_str, enum.PURCHASE_BATCH_MODE[batch.mode]), display = '{} ({}){}'.format(batch.id_str, enum.PURCHASE_BATCH_MODE[batch.mode],
self.request.route_url('purchases.batch.view', uuid=batch.uuid)) '' if batch.executed else ' (pending)')
return tags.link_to(display, self.request.route_url('purchases.batch.view', uuid=batch.uuid))
enum = self.request.rattail_config.get_enum() enum = self.request.rattail_config.get_enum()
items = [HTML.tag('li', c=render(batch)) for batch in batches] items = [HTML.tag('li', c=render(batch)) for batch in batches]
@ -65,6 +66,17 @@ class PurchaseView(MasterView):
model_row_class = model.PurchaseItem model_row_class = model.PurchaseItem
row_model_title = 'Purchase Item' row_model_title = 'Purchase Item'
def get_instance_title(self, purchase):
if purchase.status >= self.enum.PURCHASE_STATUS_RECEIVED:
if purchase.date_received:
return "{} (received {})".format(purchase.vendor, purchase.date_received.strftime('%Y-%m-%d'))
return "{} (received)".format(purchase.vendor)
elif purchase.status >= self.enum.PURCHASE_STATUS_ORDERED:
if purchase.date_ordered:
return "{} (ordered {})".format(purchase.vendor, purchase.date_ordered.strftime('%Y-%m-%d'))
return "{} (ordered)".format(purchase.vendor)
return unicode(purchase)
def _preconfigure_grid(self, g): def _preconfigure_grid(self, g):
g.joiners['store'] = lambda q: q.join(model.Store) g.joiners['store'] = lambda q: q.join(model.Store)
g.filters['store'] = g.make_filter('store', model.Store.name) g.filters['store'] = g.make_filter('store', model.Store.name)
@ -106,7 +118,8 @@ class PurchaseView(MasterView):
fs.status.set(renderer=forms.renderers.EnumFieldRenderer(enum.PURCHASE_STATUS), fs.status.set(renderer=forms.renderers.EnumFieldRenderer(enum.PURCHASE_STATUS),
readonly=True) readonly=True)
fs.po_number.set(label="PO Number") fs.po_number.set(label="PO Number")
fs.po_total.set(label="PO Total") fs.po_total.set(label="PO Total", renderer=forms.renderers.CurrencyFieldRenderer)
fs.invoice_total.set(renderer=forms.renderers.CurrencyFieldRenderer)
fs.batches.set(renderer=BatchesFieldRenderer) fs.batches.set(renderer=BatchesFieldRenderer)
def configure_fieldset(self, fs): def configure_fieldset(self, fs):
@ -114,15 +127,24 @@ class PurchaseView(MasterView):
include=[ include=[
fs.store, fs.store,
fs.vendor, fs.vendor,
fs.status,
fs.buyer, fs.buyer,
fs.date_ordered, fs.date_ordered,
fs.date_received,
fs.po_number, fs.po_number,
fs.po_total, fs.po_total,
fs.status, fs.invoice_number,
fs.invoice_total,
fs.created, fs.created,
fs.created_by, fs.created_by,
fs.batches, fs.batches,
]) ])
if self.viewing:
purchase = fs.model
if purchase.status == self.enum.PURCHASE_STATUS_ORDERED:
del fs.date_received
del fs.invoice_number
del fs.invoice_total
def get_parent(self, item): def get_parent(self, item):
return item.purchase return item.purchase
@ -136,11 +158,15 @@ class PurchaseView(MasterView):
g.sequence.set(label="Seq") g.sequence.set(label="Seq")
g.upc.set(label="UPC") g.upc.set(label="UPC")
g.brand_name.set(label="Brand") g.brand_name.set(label="Brand")
g.cases_ordered.set(label="Cases") g.cases_ordered.set(label="Cases Ord.")
g.units_ordered.set(label="Units") g.units_ordered.set(label="Units Ord.")
g.po_total.set(label="PO Total") g.cases_received.set(label="Cases Rec.")
g.units_received.set(label="Units Rec.")
g.po_total.set(label="Total", renderer=forms.renderers.CurrencyFieldRenderer)
g.invoice_total.set(label="Total", renderer=forms.renderers.CurrencyFieldRenderer)
def configure_row_grid(self, g): def configure_row_grid(self, g):
purchase = self.get_instance()
g.configure( g.configure(
include=[ include=[
g.sequence, g.sequence,
@ -150,15 +176,26 @@ class PurchaseView(MasterView):
g.size, g.size,
g.cases_ordered, g.cases_ordered,
g.units_ordered, g.units_ordered,
g.cases_received,
g.units_received,
g.po_total, g.po_total,
g.invoice_total,
], ],
readonly=True) readonly=True)
if purchase.status == enum.PURCHASE_STATUS_ORDERED:
del g.cases_received
del g.units_received
del g.invoice_total
elif purchase.status == enum.PURCHASE_STATUS_RECEIVED:
del g.po_total
def _preconfigure_row_fieldset(self, fs): def _preconfigure_row_fieldset(self, fs):
fs.vendor_code.set(label="Vendor Item Code") fs.vendor_code.set(label="Vendor Item Code")
fs.upc.set(label="UPC") fs.upc.set(label="UPC")
fs.po_unit_cost.set(label="PO Unit Cost") fs.po_unit_cost.set(label="PO Unit Cost", renderer=forms.renderers.CurrencyFieldRenderer)
fs.po_total.set(label="PO Total") fs.po_total.set(label="PO Total", renderer=forms.renderers.CurrencyFieldRenderer)
fs.invoice_unit_cost.set(renderer=forms.renderers.CurrencyFieldRenderer)
fs.invoice_total.set(renderer=forms.renderers.CurrencyFieldRenderer)
fs.append(fa.Field('department', value=lambda i: '{} {}'.format(i.department_number, i.department_name))) fs.append(fa.Field('department', value=lambda i: '{} {}'.format(i.department_number, i.department_name)))
def configure_row_fieldset(self, fs): def configure_row_fieldset(self, fs):
@ -173,8 +210,12 @@ class PurchaseView(MasterView):
fs.case_quantity, fs.case_quantity,
fs.cases_ordered, fs.cases_ordered,
fs.units_ordered, fs.units_ordered,
fs.cases_received,
fs.units_received,
fs.po_unit_cost, fs.po_unit_cost,
fs.po_total, fs.po_total,
fs.invoice_unit_cost,
fs.invoice_total,
]) ])