Add basic "pending product" support for new custorder batch

This commit is contained in:
Lance Edgar 2021-12-22 12:06:00 -06:00
parent 408bffb775
commit c0db03bc28
13 changed files with 844 additions and 234 deletions

View file

@ -623,16 +623,6 @@ class BatchMasterView(MasterView):
def get_row_status_enum(self):
return self.model_row_class.STATUS
def render_upc(self, row, field):
upc = row.upc
if not upc:
return ""
text = upc.pretty()
if row.product_uuid:
url = self.request.route_url('products.view', uuid=row.product_uuid)
return tags.link_to(text, url)
return text
def render_upc_pretty(self, row, field):
upc = getattr(row, field)
if upc:

View file

@ -510,7 +510,18 @@ class PendingCustomerView(MasterView):
f.set_enum('status_code', self.enum.PENDING_CUSTOMER_STATUS)
f.set_renderer('user', self.render_user)
# created
if self.creating:
f.remove('created')
else:
f.set_readonly('created')
# user
if self.creating:
f.remove('user')
else:
f.set_readonly('user')
f.set_renderer('user', self.render_user)
# # TODO: this is referenced by some custom apps, but should be moved??

View file

@ -74,7 +74,6 @@ class CustomerOrderBatchView(BatchMasterView):
]
row_labels = {
'product_upc': "UPC",
'product_brand': "Brand",
'product_description': "Description",
'product_size': "Size",
@ -83,7 +82,7 @@ class CustomerOrderBatchView(BatchMasterView):
row_grid_columns = [
'sequence',
'product_upc',
'_product_key_',
'product_brand',
'product_description',
'product_size',
@ -94,6 +93,38 @@ class CustomerOrderBatchView(BatchMasterView):
'status_code',
]
product_key_fields = {
'upc': 'product_upc',
'item_id': 'product_item_id',
'scancode': 'product_scancode',
}
row_form_fields = [
'sequence',
'item_entry',
'product',
'pending_product',
'_product_key_',
'product_brand',
'product_description',
'product_size',
'product_weighed',
'product_unit_of_measure',
'department_number',
'department_name',
'product_unit_cost',
'case_quantity',
'unit_price',
'price_needs_confirmation',
'order_quantity',
'order_uom',
'discount_percent',
'total_price',
'paid_amount',
# 'payment_transaction_number',
'status_code',
]
def configure_grid(self, g):
super(CustomerOrderBatchView, self).configure_grid(g)
@ -170,6 +201,8 @@ class CustomerOrderBatchView(BatchMasterView):
def row_grid_extra_class(self, row, i):
if row.status_code == row.STATUS_PRODUCT_NOT_FOUND:
return 'warning'
if row.status_code == row.STATUS_PENDING_PRODUCT:
return 'notice'
def configure_row_grid(self, g):
super(CustomerOrderBatchView, self).configure_row_grid(g)
@ -189,6 +222,9 @@ class CustomerOrderBatchView(BatchMasterView):
super(CustomerOrderBatchView, self).configure_row_form(f)
f.set_renderer('product', self.render_product)
f.set_renderer('pending_product', self.render_pending_product)
f.set_renderer('product_upc', self.render_upc)
f.set_type('case_quantity', 'quantity')
f.set_type('cases_ordered', 'quantity')
@ -197,3 +233,4 @@ class CustomerOrderBatchView(BatchMasterView):
f.set_enum('order_uom', self.enum.UNIT_OF_MEASURE)
f.set_type('unit_price', 'currency')
f.set_type('total_price', 'currency')
f.set_type('paid_amount', 'currency')

View file

@ -90,6 +90,7 @@ class CustomerOrderItemView(MasterView):
'sequence',
'person',
'product',
'pending_product',
'product_brand',
'product_description',
'product_size',
@ -178,6 +179,9 @@ class CustomerOrderItemView(MasterView):
# product
f.set_renderer('product', self.render_product)
# pending_product
f.set_renderer('pending_product', self.render_pending_product)
# product uom
f.set_enum('product_unit_of_measure', self.enum.UNIT_OF_MEASURE)

View file

@ -48,6 +48,7 @@ class CustomerOrderView(MasterView):
model_class = model.CustomerOrder
route_prefix = 'custorders'
editable = False
configurable = True
labels = {
'id': "ID",
@ -96,6 +97,10 @@ class CustomerOrderView(MasterView):
'status_code',
]
def __init__(self, request):
super(CustomerOrderView, self).__init__(request)
self.batch_handler = self.get_batch_handler()
def query(self, session):
return session.query(model.CustomerOrder)\
.options(orm.joinedload(model.CustomerOrder.customer))
@ -241,7 +246,8 @@ class CustomerOrderView(MasterView):
submits the order, at which point the batch is converted to a proper
order.
"""
self.handler = self.get_batch_handler()
# TODO: deprecate / remove this
self.handler = self.batch_handler
batch = self.get_current_batch()
if self.request.method == 'POST':
@ -285,16 +291,30 @@ class CustomerOrderView(MasterView):
context.update({
'batch': batch,
'normalized_batch': self.normalize_batch(batch),
'new_order_requires_customer': self.handler.new_order_requires_customer(),
'product_price_may_be_questionable': self.handler.product_price_may_be_questionable(),
'allow_contact_info_choice': self.handler.allow_contact_info_choice(),
'restrict_contact_info': self.handler.should_restrict_contact_info(),
'new_order_requires_customer': self.batch_handler.new_order_requires_customer(),
'product_key_field': self.rattail_config.product_key(),
'product_price_may_be_questionable': self.batch_handler.product_price_may_be_questionable(),
'allow_contact_info_choice': self.batch_handler.allow_contact_info_choice(),
'allow_contact_info_create': not self.batch_handler.allow_contact_info_creation(),
'order_items': items,
'product_key_label': self.rattail_config.product_key_title(),
'allow_unknown_product': self.batch_handler.allow_unknown_product(),
'department_options': self.get_department_options(),
})
return self.render_to_response(template, context)
def get_department_options(self):
model = self.model
departments = self.Session.query(model.Department)\
.order_by(model.Department.name)\
.all()
options = []
for department in departments:
options.append({'label': department.name,
'value': department.uuid})
return options
def get_current_batch(self):
user = self.request.user
if not user:
@ -311,7 +331,7 @@ class CustomerOrderView(MasterView):
except orm.exc.NoResultFound:
# no batch yet for this user, so make one
batch = self.handler.make_batch(
batch = self.batch_handler.make_batch(
self.Session(), created_by=user,
mode=self.enum.CUSTORDER_BATCH_MODE_CREATING)
self.Session.add(batch)
@ -320,16 +340,7 @@ class CustomerOrderView(MasterView):
return batch
def start_over_entirely(self, batch):
# delete pending customer if present
pending = batch.pending_customer
if pending:
batch.pending_customer = None
self.Session.delete(pending)
# just delete current batch outright
# TODO: should use self.handler.do_delete() instead?
self.Session.delete(batch)
self.batch_handler.do_delete(batch)
self.Session.flush()
# send user back to normal "create" page; a new batch will be generated
@ -339,16 +350,7 @@ class CustomerOrderView(MasterView):
return self.redirect(url)
def delete_batch(self, batch):
# delete pending customer if present
pending = batch.pending_customer
if pending:
batch.pending_customer = None
self.Session.delete(pending)
# just delete current batch outright
# TODO: should use self.handler.do_delete() instead?
self.Session.delete(batch)
self.batch_handler.do_delete(batch)
self.Session.flush()
# set flash msg just to be more obvious
@ -363,19 +365,21 @@ class CustomerOrderView(MasterView):
"""
Customer autocomplete logic, which invokes the handler.
"""
self.handler = self.get_batch_handler()
# TODO: deprecate / remove this
self.handler = self.batch_handler
term = self.request.GET['term']
return self.handler.customer_autocomplete(self.Session(), term,
user=self.request.user)
return self.batch_handler.customer_autocomplete(self.Session(), term,
user=self.request.user)
def person_autocomplete(self):
"""
Person autocomplete logic, which invokes the handler.
"""
self.handler = self.get_batch_handler()
# TODO: deprecate / remove this
self.handler = self.batch_handler
term = self.request.GET['term']
return self.handler.person_autocomplete(self.Session(), term,
user=self.request.user)
return self.batch_handler.person_autocomplete(self.Session(), term,
user=self.request.user)
def get_customer_info(self, batch, data):
uuid = data.get('uuid')
@ -391,7 +395,7 @@ class CustomerOrderView(MasterView):
def info_for_customer(self, batch, data, customer):
# most info comes from handler
info = self.handler.get_customer_info(batch)
info = self.batch_handler.get_customer_info(batch)
# maybe add profile URL
if info['person_uuid']:
@ -407,7 +411,7 @@ class CustomerOrderView(MasterView):
# this will either be a Person or Customer UUID
uuid = data['uuid']
if self.handler.new_order_requires_customer():
if self.batch_handler.new_order_requires_customer():
customer = self.Session.query(model.Customer).get(uuid)
if not customer:
@ -423,7 +427,7 @@ class CustomerOrderView(MasterView):
# invoke handler to assign contact
try:
self.handler.assign_contact(batch, **kwargs)
self.batch_handler.assign_contact(batch, **kwargs)
except ValueError as error:
return {'error': six.text_type(error)}
@ -439,9 +443,9 @@ class CustomerOrderView(MasterView):
'phone_number': batch.phone_number,
'contact_display': batch.contact_name,
'email_address': batch.email_address,
'contact_phones': self.handler.get_contact_phones(batch),
'contact_emails': self.handler.get_contact_emails(batch),
'contact_notes': self.handler.get_contact_notes(batch),
'contact_phones': self.batch_handler.get_contact_phones(batch),
'contact_emails': self.batch_handler.get_contact_emails(batch),
'contact_notes': self.batch_handler.get_contact_notes(batch),
'add_phone_number': bool(batch.get_param('add_phone_number')),
'add_email_address': bool(batch.get_param('add_email_address')),
'contact_profile_url': None,
@ -467,7 +471,7 @@ class CustomerOrderView(MasterView):
# we have a pending customer then it's definitely *not* known,
# but if no pending customer yet then we can still "assume" it
# is known, by default, until user specifies otherwise.
contact = self.handler.get_contact(batch)
contact = self.batch_handler.get_contact(batch)
if contact:
context['contact_is_known'] = True
else:
@ -482,7 +486,7 @@ class CustomerOrderView(MasterView):
return context
def unassign_contact(self, batch, data):
self.handler.unassign_contact(batch)
self.batch_handler.unassign_contact(batch)
self.Session.flush()
context = self.get_context_contact(batch)
context['success'] = True
@ -524,7 +528,8 @@ class CustomerOrderView(MasterView):
def update_pending_customer(self, batch, data):
try:
self.handler.update_pending_customer(batch, self.request.user, data)
self.batch_handler.update_pending_customer(batch, self.request.user,
data)
except Exception as error:
return {'error': six.text_type(error)}
@ -562,11 +567,14 @@ class CustomerOrderView(MasterView):
return self.info_for_product(batch, data, product)
def uom_choices_for_product(self, product):
return self.handler.uom_choices_for_product(product)
return self.batch_handler.uom_choices_for_product(product)
def uom_choices_for_row(self, row):
return self.batch_handler.uom_choices_for_row(row)
def info_for_product(self, batch, data, product):
try:
info = self.handler.get_product_info(batch, product)
info = self.batch_handler.get_product_info(batch, product)
except Exception as error:
return {'error': six.text_type(error)}
else:
@ -574,12 +582,12 @@ class CustomerOrderView(MasterView):
return info
def get_past_items(self, batch, data):
past_products = self.handler.get_past_products(batch)
past_products = self.batch_handler.get_past_products(batch)
past_items = []
for product in past_products:
try:
item = self.handler.get_product_info(batch, product)
item = self.batch_handler.get_product_info(batch, product)
except:
# nb. handler may raise error if product is "unsupported"
pass
@ -613,22 +621,20 @@ class CustomerOrderView(MasterView):
def normalize_row(self, row):
app = self.get_rattail_app()
products = app.get_products_handler()
products_handler = app.get_products_handler()
product = row.product
department = product.department if product else None
cost = product.cost if product else None
data = {
'uuid': row.uuid,
'sequence': row.sequence,
'item_entry': row.item_entry,
'product_uuid': row.product_uuid,
'product_upc': six.text_type(row.product_upc or ''),
'product_item_id': row.product_item_id,
'product_scancode': row.product_scancode,
'product_upc_pretty': row.product_upc.pretty() if row.product_upc else None,
'product_brand': row.product_brand,
'product_description': row.product_description,
'product_size': row.product_size,
'product_full_description': product.full_description if product else row.product_description,
'product_weighed': row.product_weighed,
'case_quantity': pretty_quantity(row.case_quantity),
@ -636,38 +642,88 @@ class CustomerOrderView(MasterView):
'units_ordered': pretty_quantity(row.units_ordered),
'order_quantity': pretty_quantity(row.order_quantity),
'order_uom': row.order_uom,
'order_uom_choices': self.uom_choices_for_product(product),
'order_uom_choices': self.uom_choices_for_row(row),
'department_display': department.name if department else None,
'vendor_display': cost.vendor.name if cost else None,
'department_display': row.department_name,
'unit_price': six.text_type(row.unit_price) if row.unit_price is not None else None,
'unit_price': float(row.unit_price) if row.unit_price is not None else None,
'unit_price_display': self.get_unit_price_display(row),
'total_price': six.text_type(row.total_price) if row.total_price is not None else None,
'total_price_display': "${:0.2f}".format(row.total_price) if row.total_price is not None else None,
'total_price': float(row.total_price) if row.total_price is not None else None,
'total_price_display': app.render_currency(row.total_price),
'status_code': row.status_code,
'status_text': row.status_text,
}
case_price = self.handler.get_case_price_for_row(row)
data['case_price'] = six.text_type(case_price) if case_price is not None else None
if row.unit_regular_price:
data['unit_regular_price'] = float(row.unit_regular_price)
data['unit_regular_price_display'] = app.render_currency(row.unit_regular_price)
if row.unit_sale_price:
data['unit_sale_price'] = float(row.unit_sale_price)
data['unit_sale_price_display'] = app.render_currency(row.unit_sale_price)
if row.sale_ends:
sale_ends = app.localtime(row.sale_ends, from_utc=True).date()
data['sale_ends'] = six.text_type(sale_ends)
data['sale_ends_display'] = app.render_date(sale_ends)
if row.unit_sale_price and row.unit_price == row.unit_sale_price:
data['pricing_reflects_sale'] = True
if row.product or row.pending_product:
data['product_full_description'] = products_handler.make_full_description(
row.product or row.pending_product)
if row.product:
cost = row.product.cost
if cost:
data['vendor_display'] = cost.vendor.name
elif row.pending_product:
data['vendor_display'] = row.pending_product.vendor_name
if row.pending_product:
pending = row.pending_product
data['pending_product'] = {
'uuid': pending.uuid,
'upc': six.text_type(pending.upc) if pending.upc is not None else None,
'item_id': pending.item_id,
'scancode': pending.scancode,
'brand_name': pending.brand_name,
'description': pending.description,
'size': pending.size,
'department_uuid': pending.department_uuid,
'regular_price_amount': float(pending.regular_price_amount) if pending.regular_price_amount is not None else None,
'vendor_name': pending.vendor_name,
'vendor_item_code': pending.vendor_item_code,
'unit_cost': float(pending.unit_cost) if pending.unit_cost is not None else None,
'case_size': float(pending.case_size) if pending.case_size is not None else None,
'notes': pending.notes,
}
case_price = self.batch_handler.get_case_price_for_row(row)
data['case_price'] = float(case_price) if case_price is not None else None
data['case_price_display'] = app.render_currency(case_price)
if self.handler.product_price_may_be_questionable():
if self.batch_handler.product_price_may_be_questionable():
data['price_needs_confirmation'] = row.price_needs_confirmation
key = self.rattail_config.product_key()
if key == 'upc':
data['product_key'] = data['product_upc_pretty']
else:
data['product_key'] = getattr(product, key, data['product_upc_pretty'])
elif key == 'item_id':
data['product_key'] = row.product_item_id
elif key == 'scancode':
data['product_key'] = row.product_scancode
else: # TODO: this seems not useful
data['product_key'] = getattr(row.product, key, data['product_upc_pretty'])
if row.product:
data.update({
'product_url': self.request.route_url('products.view', uuid=row.product.uuid),
'product_image_url': products.get_image_url(row.product),
'product_image_url': products_handler.get_image_url(row.product),
})
elif row.product_upc:
data['product_image_url'] = products_handler.get_image_url(upc=row.product_upc)
unit_uom = self.enum.UNIT_OF_MEASURE_POUND if data['product_weighed'] else self.enum.UNIT_OF_MEASURE_EACH
if row.order_uom == self.enum.UNIT_OF_MEASURE_CASE:
@ -695,6 +751,11 @@ class CustomerOrderView(MasterView):
return data
def add_item(self, batch, data):
app = self.get_rattail_app()
order_quantity = decimal.Decimal(data.get('order_quantity') or '0')
order_uom = data.get('order_uom')
if data.get('product_is_known'):
uuid = data.get('product_uuid')
@ -706,18 +767,30 @@ class CustomerOrderView(MasterView):
return {'error': "Product not found"}
kwargs = {}
if self.handler.product_price_may_be_questionable():
if self.batch_handler.product_price_may_be_questionable():
kwargs['price_needs_confirmation'] = data.get('price_needs_confirmation')
row = self.handler.add_product(batch, product,
decimal.Decimal(data.get('order_quantity') or '0'),
data.get('order_uom'),
**kwargs)
self.Session.flush()
row = self.batch_handler.add_product(batch, product,
order_quantity, order_uom,
**kwargs)
else: # product is not known
raise NotImplementedError # TODO
else: # unknown product; add pending
pending_info = dict(data['pending_product'])
if 'upc' in pending_info:
pending_info['upc'] = app.make_gpc(pending_info['upc'])
for field in ('unit_cost', 'regular_price_amount', 'case_size'):
if field in pending_info:
pending_info[field] = decimal.Decimal(pending_info[field])
pending_info['user'] = self.request.user
row = self.batch_handler.add_pending_product(batch,
pending_info,
order_quantity, order_uom)
self.Session.flush()
return {'batch': self.normalize_batch(batch),
'row': self.normalize_row(row)}
@ -733,6 +806,9 @@ class CustomerOrderView(MasterView):
if row not in batch.active_rows():
return {'error': "Row is not active for the batch"}
order_quantity = decimal.Decimal(data.get('order_quantity') or '0')
order_uom = data.get('order_uom')
if data.get('product_is_known'):
uuid = data.get('product_uuid')
@ -745,19 +821,26 @@ class CustomerOrderView(MasterView):
row.item_entry = product.uuid
row.product = product
row.order_quantity = decimal.Decimal(data.get('order_quantity') or '0')
row.order_uom = data.get('order_uom')
row.order_quantity = order_quantity
row.order_uom = order_uom
if self.handler.product_price_may_be_questionable():
if self.batch_handler.product_price_may_be_questionable():
row.price_needs_confirmation = data.get('price_needs_confirmation')
self.handler.refresh_row(row)
self.Session.flush()
self.Session.refresh(row)
self.batch_handler.refresh_row(row)
else: # product is not known
raise NotImplementedError # TODO
# set these first, since row will be refreshed below
row.order_quantity = order_quantity
row.order_uom = order_uom
# nb. this will refresh the row
pending_info = dict(data['pending_product'])
self.batch_handler.update_pending_product(row, pending_info)
self.Session.flush()
self.Session.refresh(row)
return {'batch': self.normalize_batch(batch),
'row': self.normalize_row(row)}
@ -774,7 +857,7 @@ class CustomerOrderView(MasterView):
if row not in batch.active_rows():
return {'error': "Row is not active for this batch"}
self.handler.do_remove_row(row)
self.batch_handler.do_remove_row(row)
return {'ok': True,
'batch': self.normalize_batch(batch)}
@ -794,7 +877,30 @@ class CustomerOrderView(MasterView):
return {'ok': True, 'next_url': next_url}
def execute_new_order_batch(self, batch, data):
return self.handler.do_execute(batch, self.request.user)
return self.batch_handler.do_execute(batch, self.request.user)
def configure_get_simple_settings(self):
return [
# customer handling
{'section': 'rattail.custorders',
'option': 'new_order_requires_customer',
'type': bool},
{'section': 'rattail.custorders',
'option': 'new_orders.allow_contact_info_choice',
'type': bool},
{'section': 'rattail.custorders',
'option': 'new_orders.allow_contact_info_create',
'type': bool},
# product handling
{'section': 'rattail.custorders',
'option': 'allow_unknown_product',
'type': bool},
{'section': 'rattail.custorders',
'option': 'product_price_may_be_questionable',
'type': bool},
]
@classmethod
def defaults(cls, config):

View file

@ -157,6 +157,8 @@ class MasterView(View):
labels = {'uuid': "UUID"}
product_key_fields = {}
# ROW-RELATED ATTRS FOLLOW:
has_rows = False
@ -449,6 +451,8 @@ class MasterView(View):
grid.hide_column('local_only')
grid.remove_filter('local_only')
self.configure_column_product_key(grid)
def grid_extra_class(self, obj, i):
"""
Returns string of extra class(es) for the table row corresponding to
@ -541,6 +545,8 @@ class MasterView(View):
# super(MasterView, self).configure_row_grid(grid)
self.set_row_labels(grid)
self.configure_column_product_key(grid)
def row_grid_extra_class(self, obj, i):
"""
Returns string of extra class(es) for the table row corresponding to
@ -753,6 +759,7 @@ class MasterView(View):
if obj.emails:
return obj.emails[0].address
# TODO: deprecate / remove this
def render_product_key_value(self, obj, field=None):
"""
Render the "canonical" product key value for the given object.
@ -764,6 +771,15 @@ class MasterView(View):
return obj.upc.pretty() if obj.upc else ''
return getattr(obj, product_key)
def render_upc(self, obj, field):
"""
Render a :class:`~rattail:rattail.gpc.GPC` field.
"""
value = getattr(obj, field)
if value:
app = self.rattail_config.get_app()
return app.render_gpc(value)
def render_store(self, obj, field):
store = getattr(obj, field)
if store:
@ -779,6 +795,14 @@ class MasterView(View):
url = self.request.route_url('products.view', uuid=product.uuid)
return tags.link_to(text, url)
def render_pending_product(self, obj, field):
pending = getattr(obj, field)
if not pending:
return
text = six.text_type(pending)
url = self.request.route_url('pending_products.view', uuid=pending.uuid)
return tags.link_to(text, url)
def render_vendor(self, obj, field):
vendor = getattr(obj, field)
if not vendor:
@ -1567,6 +1591,8 @@ class MasterView(View):
return self.render_to_response('delete', {
'instance': instance,
'instance_title': instance_title,
'instance_editable': self.editable_instance(instance),
'instance_deletable': self.deletable_instance(instance),
'form': form})
def bulk_delete(self):
@ -3676,6 +3702,8 @@ class MasterView(View):
"""
self.configure_common_form(form)
self.configure_field_product_key(form)
def validate_form(self, form):
if form.validate(newstyle=True):
self.form_deserialized = form.validated
@ -4107,12 +4135,33 @@ class MasterView(View):
self.set_row_labels(form)
self.configure_field_product_key(form)
def validate_row_form(self, form):
if form.validate(newstyle=True):
self.form_deserialized = form.validated
return True
return False
def configure_column_product_key(self, g):
if '_product_key_' in g.columns:
key = self.rattail_config.product_key()
field = self.product_key_fields.get(key, key)
g.replace('_product_key_', field)
g.set_label(field, self.rattail_config.product_key_title(key))
g.set_link(field)
if key == 'upc':
g.set_renderer(field, self.render_upc)
def configure_field_product_key(self, f):
if '_product_key_' in f:
key = self.rattail_config.product_key()
field = self.product_key_fields.get(key, key)
f.replace('_product_key_', field)
f.set_label(field, self.rattail_config.product_key_title(key))
if key == 'upc':
f.set_renderer(field, self.render_upc)
def get_row_action_url(self, action, row, **kwargs):
"""
Generate a URL for the given action on the given row.

View file

@ -1153,6 +1153,7 @@ class ProductView(MasterView):
use_buefy = self.get_use_buefy()
kwargs['image_url'] = self.handler.get_image_url(product)
kwargs['product_key_field'] = self.rattail_config.product_key()
# add price history, if user has access
if self.rattail_config.versioning_enabled() and self.has_perm('versions'):
@ -1910,7 +1911,6 @@ class ProductView(MasterView):
return self.request.route_url('batch.delproduct.view', uuid=batch.uuid)
def configure_get_simple_settings(self):
config = self.rattail_config
return [
# key field
@ -1987,6 +1987,7 @@ class PendingProductView(MasterView):
model_class = model.PendingProduct
route_prefix = 'pending_products'
url_prefix = '/products/pending'
bulk_deletable = True
labels = {
'regular_price_amount': "Regular Price",
@ -1996,10 +1997,10 @@ class PendingProductView(MasterView):
grid_columns = [
'_product_key_',
'department_name',
'brand_name',
'description',
'size',
'department_name',
'created',
'user',
'status_code',
@ -2007,12 +2008,15 @@ class PendingProductView(MasterView):
form_fields = [
'_product_key_',
'department_name',
'department',
'brand_name',
'brand',
'description',
'size',
'department_name',
'department',
'vendor_name',
'vendor',
'unit_cost',
'case_size',
'regular_price_amount',
'special_order',
@ -2025,13 +2029,6 @@ class PendingProductView(MasterView):
def configure_grid(self, g):
super(PendingProductView, self).configure_grid(g)
# product key
if '_product_key_' in g.columns:
key = self.rattail_config.product_key()
g.replace('_product_key_', key)
g.set_label(key, self.rattail_config.product_key_title(key))
g.set_link(key)
g.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS)
g.set_sort_defaults('created', 'desc')
@ -2043,13 +2040,6 @@ class PendingProductView(MasterView):
model = self.model
pending = f.model_instance
# product key
if '_product_key_' in f:
key = self.rattail_config.product_key()
f.replace('_product_key_', key)
f.set_label(key, self.rattail_config.product_key_title(key))
f.set_renderer(key, self.render_product_key_value)
# department
if self.creating or self.editing:
if 'department' in f:
@ -2084,10 +2074,35 @@ class PendingProductView(MasterView):
f.set_renderer('brand', self.render_brand)
if pending.brand:
f.remove('brand_name')
elif pending.brand_name:
f.remove('brand')
# description
f.set_required('description')
# vendor
if self.creating or self.editing:
if 'vendor' in f:
f.remove('vendor_name')
f.replace('vendor', 'vendor_uuid')
f.set_node('vendor_uuid', colander.String())
vendor_display = ""
if self.request.method == 'POST':
if self.request.POST.get('vendor_uuid'):
vendor = self.Session.query(model.Vendor).get(self.request.POST['vendor_uuid'])
if vendor:
vendor_display = six.text_type(vendor)
f.set_widget('vendor_uuid', forms.widgets.JQueryAutocompleteWidget(
field_display=vendor_display,
service_url=self.request.route_url('vendors.autocomplete')))
f.set_label('vendor_uuid', "Vendor")
else:
f.set_renderer('vendor', self.render_vendor)
if pending.vendor:
f.remove('vendor_name')
elif pending.vendor_name:
f.remove('vendor')
# case_size
f.set_type('case_size', 'quantity')
@ -2138,6 +2153,30 @@ class PendingProductView(MasterView):
return pending
def before_delete(self, pending):
"""
Event hook, called just before deletion is attempted.
"""
model = self.model
model_title = self.get_model_title()
count = self.Session.query(model.CustomerOrderItem)\
.filter(model.CustomerOrderItem.pending_product == pending)\
.count()
if count:
self.request.session.flash("Cannot delete this {} because it is still "
"referenced by {} Customer Orders.".format(model_title, count),
'error')
return self.redirect(self.get_action_url('view', pending))
count = self.Session.query(model.CustomerOrderBatchRow)\
.filter(model.CustomerOrderBatchRow.pending_product == pending)\
.count()
if count:
self.request.session.flash("Cannot delete this {} because it is still "
"referenced by {} \"new\" Customer Order Batches.".format(model_title, count),
'error')
return self.redirect(self.get_action_url('view', pending))
def print_labels(request):
profile = request.params.get('profile')