Add support for 'department' field in purchases / batches

Also fix logic for deleting a purchase (delete its batches first)
This commit is contained in:
Lance Edgar 2016-12-09 14:01:06 -06:00
parent 6c3d221e98
commit c73ba56505
6 changed files with 62 additions and 13 deletions

View file

@ -43,8 +43,8 @@ from .users import UserFieldRenderer, PermissionsFieldRenderer
from .employees import EmployeeFieldRenderer from .employees import EmployeeFieldRenderer
from .products import (ProductFieldRenderer, GPCFieldRenderer, BrandFieldRenderer, from .products import (GPCFieldRenderer, DepartmentFieldRenderer, BrandFieldRenderer,
PriceFieldRenderer, PriceWithExpirationFieldRenderer) ProductFieldRenderer, PriceFieldRenderer, PriceWithExpirationFieldRenderer)
from .stores import StoreFieldRenderer from .stores import StoreFieldRenderer

View file

@ -83,11 +83,16 @@ class DepartmentFieldRenderer(SelectFieldRenderer):
""" """
Shows the department number as well as the name. Shows the department number as well as the name.
""" """
def render_readonly(self, **kwargs): def render_readonly(self, **kwargs):
dept = self.raw_value department = self.raw_value
if dept: if not department:
return "{0} - {1}".format(dept.number, dept.name) return ''
return "" if department.number:
text = '{} {}'.format(department.number, department.name)
else:
text = department.name
return tags.link_to(text, self.request.route_url('departments.view', uuid=department.uuid))
class SubdepartmentFieldRenderer(SelectFieldRenderer): class SubdepartmentFieldRenderer(SelectFieldRenderer):

View file

@ -9,6 +9,7 @@
if (mode == ${enum.PURCHASE_BATCH_MODE_NEW}) { if (mode == ${enum.PURCHASE_BATCH_MODE_NEW}) {
$('.field-wrapper.store_uuid').show(); $('.field-wrapper.store_uuid').show();
$('.field-wrapper.purchase_uuid').hide(); $('.field-wrapper.purchase_uuid').hide();
$('.field-wrapper.department_uuid').show();
$('.field-wrapper.buyer_uuid').show(); $('.field-wrapper.buyer_uuid').show();
$('.field-wrapper.date_ordered').show(); $('.field-wrapper.date_ordered').show();
$('.field-wrapper.date_received').hide(); $('.field-wrapper.date_received').hide();
@ -17,6 +18,7 @@
} else if (mode == ${enum.PURCHASE_BATCH_MODE_RECEIVING}) { } else if (mode == ${enum.PURCHASE_BATCH_MODE_RECEIVING}) {
$('.field-wrapper.store_uuid').hide(); $('.field-wrapper.store_uuid').hide();
$('.field-wrapper.purchase_uuid').show(); $('.field-wrapper.purchase_uuid').show();
$('.field-wrapper.department_uuid').hide();
$('.field-wrapper.buyer_uuid').hide(); $('.field-wrapper.buyer_uuid').hide();
$('.field-wrapper.date_ordered').hide(); $('.field-wrapper.date_ordered').hide();
$('.field-wrapper.date_received').show(); $('.field-wrapper.date_received').show();
@ -25,6 +27,7 @@
} else if (mode == ${enum.PURCHASE_BATCH_MODE_COSTING}) { } else if (mode == ${enum.PURCHASE_BATCH_MODE_COSTING}) {
$('.field-wrapper.store_uuid').hide(); $('.field-wrapper.store_uuid').hide();
$('.field-wrapper.purchase_uuid').show(); $('.field-wrapper.purchase_uuid').show();
$('.field-wrapper.department_uuid').hide();
$('.field-wrapper.buyer_uuid').hide(); $('.field-wrapper.buyer_uuid').hide();
$('.field-wrapper.date_ordered').hide(); $('.field-wrapper.date_ordered').hide();
$('.field-wrapper.date_received').hide(); $('.field-wrapper.date_received').hide();
@ -76,6 +79,7 @@
}); });
$('.field-wrapper.purchase_uuid select').selectmenu(); $('.field-wrapper.purchase_uuid select').selectmenu();
$('.field-wrapper.department_uuid select').selectmenu();
show_mode(${form.fieldset.model.mode or enum.PURCHASE_BATCH_MODE_NEW}); show_mode(${form.fieldset.model.mode or enum.PURCHASE_BATCH_MODE_NEW});

View file

@ -526,7 +526,12 @@ class MasterView(View):
Return a "humanized" (and plural) version of the model name, for Return a "humanized" (and plural) version of the model name, for
display in templates. display in templates.
""" """
return getattr(cls, 'model_title_plural', '{0}s'.format(cls.get_model_title())) if hasattr(cls, 'model_title_plural'):
return cls.model_title_plural
try:
return cls.get_model_class().get_model_title_plural()
except (NotImplementedError, AttributeError):
return '{}s'.format(cls.get_model_title())
@classmethod @classmethod
def get_route_prefix(cls): def get_route_prefix(cls):

View file

@ -31,7 +31,7 @@ import logging
from sqlalchemy import orm from sqlalchemy import orm
from rattail import enum, pod from rattail import pod
from rattail.db import model, api from rattail.db import model, api
from rattail.gpc import GPC from rattail.gpc import GPC
from rattail.time import localtime from rattail.time import localtime
@ -67,7 +67,6 @@ class PurchaseBatchView(BatchMasterView):
Master view for purchase order batches. Master view for purchase order batches.
""" """
model_class = model.PurchaseBatch model_class = model.PurchaseBatch
model_title_plural = "Purchase Batches"
model_row_class = model.PurchaseBatchRow model_row_class = model.PurchaseBatchRow
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler' default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
route_prefix = 'purchases.batch' route_prefix = 'purchases.batch'
@ -87,6 +86,10 @@ class PurchaseBatchView(BatchMasterView):
default_active=True, default_verb='contains') default_active=True, default_verb='contains')
g.sorters['vendor'] = g.make_sorter(model.Vendor.name) g.sorters['vendor'] = g.make_sorter(model.Vendor.name)
g.joiners['department'] = lambda q: q.join(model.Department)
g.filters['department'] = g.make_filter('department', model.Department.name)
g.sorters['department'] = g.make_sorter(model.Department.name)
g.joiners['buyer'] = lambda q: q.join(model.Employee).join(model.Person) g.joiners['buyer'] = lambda q: q.join(model.Employee).join(model.Person)
g.filters['buyer'] = g.make_filter('buyer', model.Person.display_name, g.filters['buyer'] = g.make_filter('buyer', model.Person.display_name,
default_active=True, default_verb='contains') default_active=True, default_verb='contains')
@ -106,6 +109,7 @@ class PurchaseBatchView(BatchMasterView):
g.id, g.id,
g.mode, g.mode,
g.vendor, g.vendor,
g.department,
g.buyer, g.buyer,
g.date_ordered, g.date_ordered,
g.created, g.created,
@ -121,6 +125,8 @@ class PurchaseBatchView(BatchMasterView):
fs.vendor.set(renderer=forms.renderers.VendorFieldRenderer, fs.vendor.set(renderer=forms.renderers.VendorFieldRenderer,
attrs={'selected': 'vendor_selected', attrs={'selected': 'vendor_selected',
'cleared': 'vendor_cleared'}) 'cleared': 'vendor_cleared'})
fs.department.set(renderer=forms.renderers.DepartmentFieldRenderer,
options=self.get_department_options())
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, renderer=forms.renderers.CurrencyFieldRenderer) fs.po_total.set(label="PO Total", readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
@ -135,6 +141,10 @@ class PurchaseBatchView(BatchMasterView):
fs.append(fa.Field('vendor_phone', readonly=True, fs.append(fa.Field('vendor_phone', readonly=True,
value=self.get_vendor_phone_number)) value=self.get_vendor_phone_number))
def get_department_options(self):
departments = Session.query(model.Department).order_by(model.Department.number)
return [('{} {}'.format(d.number, d.name), d.uuid) for d in departments]
def get_vendor_phone_number(self, batch): def get_vendor_phone_number(self, batch):
for phone in batch.vendor.phones: for phone in batch.vendor.phones:
if phone.type == 'Voice': if phone.type == 'Voice':
@ -152,6 +162,7 @@ class PurchaseBatchView(BatchMasterView):
fs.mode, fs.mode,
fs.store, fs.store,
fs.vendor, fs.vendor,
fs.department,
fs.purchase, fs.purchase,
fs.vendor_email, fs.vendor_email,
fs.vendor_fax, fs.vendor_fax,
@ -208,6 +219,7 @@ 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.department.set(readonly=True)
fs.purchase.set(readonly=True) fs.purchase.set(readonly=True)
def eligible_purchases(self): def eligible_purchases(self):
@ -223,10 +235,10 @@ class PurchaseBatchView(BatchMasterView):
purchases = Session.query(model.Purchase)\ purchases = Session.query(model.Purchase)\
.filter(model.Purchase.vendor == vendor) .filter(model.Purchase.vendor == vendor)
if mode == enum.PURCHASE_BATCH_MODE_RECEIVING: if mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING:
purchases = purchases.filter(model.Purchase.status == self.enum.PURCHASE_STATUS_ORDERED)\ purchases = purchases.filter(model.Purchase.status == self.enum.PURCHASE_STATUS_ORDERED)\
.order_by(model.Purchase.date_ordered, model.Purchase.created) .order_by(model.Purchase.date_ordered, model.Purchase.created)
elif mode == enum.PURCHASE_BATCH_MODE_COSTING: elif mode == self.enum.PURCHASE_BATCH_MODE_COSTING:
purchases = purchases.filter(model.Purchase.status == self.enum.PURCHASE_STATUS_RECEIVED)\ purchases = purchases.filter(model.Purchase.status == self.enum.PURCHASE_STATUS_RECEIVED)\
.order_by(model.Purchase.date_received, model.Purchase.created) .order_by(model.Purchase.date_received, model.Purchase.created)
@ -240,7 +252,7 @@ class PurchaseBatchView(BatchMasterView):
elif purchase.status == self.enum.PURCHASE_STATUS_RECEIVED: elif purchase.status == self.enum.PURCHASE_STATUS_RECEIVED:
date = purchase.date_received date = purchase.date_received
total = purchase.invoice_total total = purchase.invoice_total
return '{} for ${:0,.2f} ({})'.format(date, total, purchase.buyer) return '{} for ${:0,.2f} ({})'.format(date, total, purchase.department or 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)
@ -253,6 +265,10 @@ class PurchaseBatchView(BatchMasterView):
kwargs['vendor'] = batch.vendor kwargs['vendor'] = batch.vendor
elif batch.vendor_uuid: elif batch.vendor_uuid:
kwargs['vendor_uuid'] = batch.vendor_uuid kwargs['vendor_uuid'] = batch.vendor_uuid
if batch.department:
kwargs['department'] = batch.department
elif batch.department_uuid:
kwargs['department_uuid'] = batch.department_uuid
if batch.buyer: if batch.buyer:
kwargs['buyer'] = batch.buyer kwargs['buyer'] = batch.buyer
elif batch.buyer_uuid: elif batch.buyer_uuid:
@ -274,6 +290,8 @@ class PurchaseBatchView(BatchMasterView):
purchase = Session.query(model.Purchase).get(batch.purchase_uuid) purchase = Session.query(model.Purchase).get(batch.purchase_uuid)
assert purchase assert purchase
kwargs['purchase'] = purchase kwargs['purchase'] = purchase
kwargs['buyer'] = purchase.buyer
kwargs['buyer_uuid'] = purchase.buyer_uuid
kwargs['date_ordered'] = purchase.date_ordered kwargs['date_ordered'] = purchase.date_ordered
kwargs['po_total'] = purchase.po_total kwargs['po_total'] = purchase.po_total
@ -510,7 +528,7 @@ class PurchaseBatchView(BatchMasterView):
history = OrderedDict() history = OrderedDict()
purchases = Session.query(model.Purchase)\ purchases = Session.query(model.Purchase)\
.filter(model.Purchase.vendor == batch.vendor)\ .filter(model.Purchase.vendor == batch.vendor)\
.filter(model.Purchase.status >= enum.PURCHASE_STATUS_ORDERED)\ .filter(model.Purchase.status >= self.enum.PURCHASE_STATUS_ORDERED)\
.order_by(model.Purchase.date_ordered.desc(), model.Purchase.created.desc())\ .order_by(model.Purchase.date_ordered.desc(), model.Purchase.created.desc())\
.options(orm.joinedload(model.Purchase.items))[:6] .options(orm.joinedload(model.Purchase.items))[:6]
for purchase in purchases[:6]: for purchase in purchases[:6]:

View file

@ -100,6 +100,10 @@ class PurchaseView(MasterView):
default_active=True, default_verb='contains') default_active=True, default_verb='contains')
g.sorters['vendor'] = g.make_sorter(model.Vendor.name) g.sorters['vendor'] = g.make_sorter(model.Vendor.name)
g.joiners['department'] = lambda q: q.join(model.Department)
g.filters['department'] = g.make_filter('department', model.Department.name)
g.sorters['department'] = g.make_sorter(model.Department.name)
g.joiners['buyer'] = lambda q: q.join(model.Employee).join(model.Person) g.joiners['buyer'] = lambda q: q.join(model.Employee).join(model.Person)
g.filters['buyer'] = g.make_filter('buyer', model.Person.display_name, g.filters['buyer'] = g.make_filter('buyer', model.Person.display_name,
default_active=True, default_verb='contains') default_active=True, default_verb='contains')
@ -121,6 +125,7 @@ class PurchaseView(MasterView):
include=[ include=[
g.store, g.store,
g.vendor, g.vendor,
g.department,
g.buyer, g.buyer,
g.date_ordered, g.date_ordered,
g.date_received, g.date_received,
@ -130,6 +135,7 @@ class PurchaseView(MasterView):
def _preconfigure_fieldset(self, fs): def _preconfigure_fieldset(self, fs):
fs.vendor.set(renderer=forms.renderers.VendorFieldRenderer) fs.vendor.set(renderer=forms.renderers.VendorFieldRenderer)
fs.department.set(renderer=forms.renderers.DepartmentFieldRenderer)
fs.status.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.PURCHASE_STATUS), fs.status.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.PURCHASE_STATUS),
readonly=True) readonly=True)
fs.po_number.set(label="PO Number") fs.po_number.set(label="PO Number")
@ -142,6 +148,7 @@ class PurchaseView(MasterView):
include=[ include=[
fs.store, fs.store,
fs.vendor, fs.vendor,
fs.department,
fs.status, fs.status,
fs.buyer, fs.buyer,
fs.date_ordered, fs.date_ordered,
@ -162,6 +169,16 @@ class PurchaseView(MasterView):
del fs.invoice_number del fs.invoice_number
del fs.invoice_total del fs.invoice_total
def delete_instance(self, purchase):
"""
Delete all batches for the purchase, then delete the purchase.
"""
for batch in list(purchase.batches):
self.Session.delete(batch)
self.Session.flush()
self.Session.delete(purchase)
self.Session.flush()
def get_parent(self, item): def get_parent(self, item):
return item.purchase return item.purchase