Add support for 'department' field in purchases / batches
Also fix logic for deleting a purchase (delete its batches first)
This commit is contained in:
parent
6c3d221e98
commit
c73ba56505
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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});
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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]:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue