Add (restore?) basic support for mobile receiving from PO
This commit is contained in:
parent
87ba8026e5
commit
34bdd2ac84
|
@ -57,7 +57,7 @@ function setfocus() {
|
||||||
var queries = [
|
var queries = [
|
||||||
'#username',
|
'#username',
|
||||||
'#new-purchasing-batch-vendor-text',
|
'#new-purchasing-batch-vendor-text',
|
||||||
// '.receiving-upc-search',
|
'#new-receiving-batch-vendor-text',
|
||||||
];
|
];
|
||||||
$.each(queries, function(i, query) {
|
$.each(queries, function(i, query) {
|
||||||
el = $(query);
|
el = $(query);
|
||||||
|
@ -92,16 +92,6 @@ $(document).on('click', 'form[name="new-purchasing-batch"] input[type="submit"]'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// submit new purchasing batch form on Purchase click
|
|
||||||
$(document).on('click', 'form[name="new-purchasing-batch"] [data-role="listview"] a', function() {
|
|
||||||
var $form = $(this).parents('form');
|
|
||||||
var $field = $form.find('[name="purchase"]');
|
|
||||||
var uuid = $(this).parents('li').data('uuid');
|
|
||||||
$field.val(uuid);
|
|
||||||
$form.submit();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// disable datasync restart button when clicked
|
// disable datasync restart button when clicked
|
||||||
$(document).on('click', '#datasync-restart', function() {
|
$(document).on('click', '#datasync-restart', function() {
|
||||||
|
|
|
@ -8,29 +8,31 @@
|
||||||
************************************************************/
|
************************************************************/
|
||||||
|
|
||||||
|
|
||||||
// TODO: this is really just for receiving; should change form name?
|
// toggle visibility of "Receive" type buttons based on whether vendor is set
|
||||||
$(document).on('autocompleteitemselected', 'form[name="new-purchasing-batch"] .vendor', function(event, uuid) {
|
$(document).on('autocompleteitemselected', 'form[name="new-receiving-batch"] .vendor', function(event, uuid) {
|
||||||
$('#new-receiving-types').show();
|
$('#new-receiving-types').show();
|
||||||
});
|
});
|
||||||
|
$(document).on('autocompleteitemcleared', 'form[name="new-receiving-batch"] .vendor', function(event) {
|
||||||
|
|
||||||
// TODO: this is really just for receiving; should change form name?
|
|
||||||
$(document).on('autocompleteitemcleared', 'form[name="new-purchasing-batch"] .vendor', function(event) {
|
|
||||||
$('#new-receiving-types').hide();
|
$('#new-receiving-types').hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
$(document).on('click', 'form[name="new-purchasing-batch"] #receive-truck-dump', function() {
|
// submit new receiving batch form when user clicks "Receive" type button
|
||||||
|
$(document).on('click', 'form[name="new-receiving-batch"] .start-receiving', function() {
|
||||||
var form = $(this).parents('form');
|
var form = $(this).parents('form');
|
||||||
form.find('input[name="workflow"]').val('truck_dump');
|
form.find('input[name="workflow"]').val($(this).data('workflow'));
|
||||||
form.submit();
|
form.submit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
$(document).on('click', 'form[name="new-purchasing-batch"] #receive-from-scratch', function() {
|
// submit new receiving batch form when user clicks Purchase Order option
|
||||||
|
$(document).on('click', 'form[name="new-receiving-batch"] [data-role="listview"] a', function() {
|
||||||
var form = $(this).parents('form');
|
var form = $(this).parents('form');
|
||||||
form.find('input[name="workflow"]').val('from_scratch');
|
var key = $(this).parents('li').data('key');
|
||||||
|
form.find('[name="workflow"]').val('from_po');
|
||||||
|
form.find('.purchase-order-field').val(key);
|
||||||
form.submit();
|
form.submit();
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,16 @@
|
||||||
|
|
||||||
<%def name="page_title()">${h.link_to("Receiving", url('mobile.receiving'))} » New Batch</%def>
|
<%def name="page_title()">${h.link_to("Receiving", url('mobile.receiving'))} » New Batch</%def>
|
||||||
|
|
||||||
${h.form(request.current_route_url(), class_='ui-filterable', name='new-purchasing-batch')}
|
${h.form(form.action_url, class_='ui-filterable', name='new-receiving-batch')}
|
||||||
${h.csrf_token(request)}
|
${h.csrf_token(request)}
|
||||||
|
|
||||||
% if vendor is Undefined:
|
% if phase == 1:
|
||||||
|
|
||||||
<div class="field-wrapper vendor">
|
<div class="field-wrapper vendor">
|
||||||
<div class="field autocomplete" data-url="${url('vendors.autocomplete')}">
|
<div class="field autocomplete" data-url="${url('vendors.autocomplete')}">
|
||||||
${h.hidden('vendor')}
|
${h.hidden('vendor')}
|
||||||
${h.text('new-purchasing-batch-vendor-text', placeholder="Vendor name", autocomplete='off', **{'data-type': 'search'})}
|
${h.text('new-receiving-batch-vendor-text', placeholder="Vendor name", autocomplete='off', **{'data-type': 'search'})}
|
||||||
<ul data-role="listview" data-inset="true" data-filter="true" data-input="#new-purchasing-batch-vendor-text"></ul>
|
<ul data-role="listview" data-inset="true" data-filter="true" data-input="#new-receiving-batch-vendor-text"></ul>
|
||||||
<button type="button" style="display: none;">Change Vendor</button>
|
<button type="button" style="display: none;">Change Vendor</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,25 +24,29 @@ ${h.csrf_token(request)}
|
||||||
<div id="new-receiving-types" style="display: none;">
|
<div id="new-receiving-types" style="display: none;">
|
||||||
|
|
||||||
${h.hidden('workflow')}
|
${h.hidden('workflow')}
|
||||||
|
${h.hidden('phase', value='1')}
|
||||||
|
|
||||||
% if master.allow_from_po:
|
% if master.allow_from_po:
|
||||||
## ${h.submit('submit', "Find purchase orders")}
|
<button type="button" class="start-receiving" data-workflow="from_po">Receive from PO</button>
|
||||||
<button type="button">Receive from PO</button>
|
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
% if master.allow_from_scratch:
|
% if master.allow_from_scratch:
|
||||||
<button type="button" id="receive-from-scratch">Receive from Scratch</button>
|
<button type="button" class="start-receiving" data-workflow="from_scratch">Receive from Scratch</button>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
% if master.allow_truck_dump:
|
% if master.allow_truck_dump:
|
||||||
<button type="button" id="receive-truck-dump">Receive Truck Dump</button>
|
<button type="button" class="start-receiving" data-workflow="truck_dump">Receive Truck Dump</button>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
% else: ## vendor is known
|
% else: ## phase 2
|
||||||
|
|
||||||
|
${h.hidden('workflow')}
|
||||||
|
${h.hidden('phase', value='2')}
|
||||||
|
|
||||||
<div class="field-wrapper vendor">
|
<div class="field-wrapper vendor">
|
||||||
|
<label>Vendor</label>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
${h.hidden('vendor', value=vendor.uuid)}
|
${h.hidden('vendor', value=vendor.uuid)}
|
||||||
${vendor}
|
${vendor}
|
||||||
|
@ -50,17 +54,22 @@ ${h.csrf_token(request)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
% if purchases:
|
% if purchases:
|
||||||
${h.hidden('purchase')}
|
${h.hidden(purchase_order_fieldname, class_='purchase-order-field')}
|
||||||
|
<p>Please choose a Purchase Order to receive:</p>
|
||||||
<ul data-role="listview" data-inset="true">
|
<ul data-role="listview" data-inset="true">
|
||||||
% for uuid, purchase in purchases:
|
% for key, purchase in purchases:
|
||||||
<li data-uuid="${uuid}">${h.link_to(purchase, '#')}</li>
|
<li data-key="${key}">${h.link_to(purchase, '#')}</li>
|
||||||
% endfor
|
% endfor
|
||||||
</ul>
|
</ul>
|
||||||
% else:
|
% else:
|
||||||
<p>(no eligible purchases found)</p>
|
<p>(no eligible purchases found)</p>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
## ${h.link_to("Receive from scratch for {}".format(vendor), '#', class_='ui-btn ui-corner-all')}
|
% if master.allow_from_scratch:
|
||||||
|
<button type="button" class="start-receiving" data-workflow="from_scratch">Receive from Scratch</button>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
${h.link_to("Cancel", url('mobile.{}'.format(route_prefix)), class_='ui-btn ui-corner-all')}
|
||||||
|
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
|
|
|
@ -560,9 +560,11 @@ class PurchasingBatchView(BatchMasterView):
|
||||||
|
|
||||||
if self.batch_mode in (self.enum.PURCHASE_BATCH_MODE_RECEIVING,
|
if self.batch_mode in (self.enum.PURCHASE_BATCH_MODE_RECEIVING,
|
||||||
self.enum.PURCHASE_BATCH_MODE_COSTING):
|
self.enum.PURCHASE_BATCH_MODE_COSTING):
|
||||||
if batch.purchase_uuid:
|
purchase = batch.purchase
|
||||||
|
if not purchase and batch.purchase_uuid:
|
||||||
purchase = self.Session.query(model.Purchase).get(batch.purchase_uuid)
|
purchase = self.Session.query(model.Purchase).get(batch.purchase_uuid)
|
||||||
assert purchase
|
assert purchase
|
||||||
|
if purchase:
|
||||||
kwargs['purchase'] = purchase
|
kwargs['purchase'] = purchase
|
||||||
kwargs['buyer'] = purchase.buyer
|
kwargs['buyer'] = purchase.buyer
|
||||||
kwargs['buyer_uuid'] = purchase.buyer_uuid
|
kwargs['buyer_uuid'] = purchase.buyer_uuid
|
||||||
|
|
|
@ -66,8 +66,19 @@ class MobileItemStatusFilter(grids.filters.MobileFilter):
|
||||||
|
|
||||||
# TODO: is this accurate (enough) ?
|
# TODO: is this accurate (enough) ?
|
||||||
if value == 'incomplete':
|
if value == 'incomplete':
|
||||||
return query.filter(sa.or_(model.PurchaseBatchRow.cases_ordered != 0, model.PurchaseBatchRow.units_ordered != 0))\
|
return query.filter(sa.or_(model.PurchaseBatchRow.cases_ordered != 0,
|
||||||
.filter(model.PurchaseBatchRow.status_code != model.PurchaseBatchRow.STATUS_OK)
|
model.PurchaseBatchRow.units_ordered != 0))\
|
||||||
|
.filter(~model.PurchaseBatchRow.status_code.in_((
|
||||||
|
model.PurchaseBatchRow.STATUS_OK,
|
||||||
|
model.PurchaseBatchRow.STATUS_PRODUCT_NOT_FOUND)))
|
||||||
|
|
||||||
|
if value == 'invalid':
|
||||||
|
return query.filter(model.PurchaseBatchRow.status_code.in_((
|
||||||
|
model.PurchaseBatchRow.STATUS_PRODUCT_NOT_FOUND,
|
||||||
|
model.PurchaseBatchRow.STATUS_COST_NOT_FOUND,
|
||||||
|
model.PurchaseBatchRow.STATUS_CASE_QUANTITY_UNKNOWN,
|
||||||
|
model.PurchaseBatchRow.STATUS_CASE_QUANTITY_DIFFERS,
|
||||||
|
)))
|
||||||
|
|
||||||
if value == 'unexpected':
|
if value == 'unexpected':
|
||||||
return query.filter(sa.and_(
|
return query.filter(sa.and_(
|
||||||
|
@ -118,6 +129,8 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
|
|
||||||
default_uom_is_case = True
|
default_uom_is_case = True
|
||||||
|
|
||||||
|
purchase_order_fieldname = 'purchase'
|
||||||
|
|
||||||
labels = {
|
labels = {
|
||||||
'truck_dump_batch': "Truck Dump Parent",
|
'truck_dump_batch': "Truck Dump Parent",
|
||||||
'invoice_parser_key': "Invoice Parser",
|
'invoice_parser_key': "Invoice Parser",
|
||||||
|
@ -363,18 +376,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
|
|
||||||
def get_batch_kwargs(self, batch, mobile=False):
|
def get_batch_kwargs(self, batch, mobile=False):
|
||||||
kwargs = super(ReceivingBatchView, self).get_batch_kwargs(batch, mobile=mobile)
|
kwargs = super(ReceivingBatchView, self).get_batch_kwargs(batch, mobile=mobile)
|
||||||
|
if not mobile:
|
||||||
if mobile:
|
|
||||||
if 'purchase' in self.request.POST:
|
|
||||||
purchase = self.get_purchase(self.request.POST['purchase'])
|
|
||||||
if isinstance(purchase, model.Purchase):
|
|
||||||
kwargs['purchase'] = purchase
|
|
||||||
|
|
||||||
department = self.department_for_purchase(purchase)
|
|
||||||
if department:
|
|
||||||
kwargs['department'] = department
|
|
||||||
|
|
||||||
else: # not mobile
|
|
||||||
batch_type = self.request.POST['batch_type']
|
batch_type = self.request.POST['batch_type']
|
||||||
if batch_type == 'from_scratch':
|
if batch_type == 'from_scratch':
|
||||||
kwargs.pop('truck_dump_batch', None)
|
kwargs.pop('truck_dump_batch', None)
|
||||||
|
@ -516,10 +518,10 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
|
|
||||||
# visible filter options will depend on whether batch came from purchase
|
# visible filter options will depend on whether batch came from purchase
|
||||||
if batch.order_quantities_known:
|
if batch.order_quantities_known:
|
||||||
value_choices = ['incomplete', 'unexpected', 'damaged', 'expired', 'all']
|
value_choices = ['incomplete', 'unexpected', 'damaged', 'expired', 'invalid', 'all']
|
||||||
default_status = 'incomplete'
|
default_status = 'incomplete'
|
||||||
else:
|
else:
|
||||||
value_choices = ['received', 'damaged', 'expired', 'all']
|
value_choices = ['received', 'damaged', 'expired', 'invalid', 'all']
|
||||||
default_status = 'all'
|
default_status = 'all'
|
||||||
|
|
||||||
# remove 'expired' filter option if not relevant
|
# remove 'expired' filter option if not relevant
|
||||||
|
@ -540,10 +542,12 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
"""
|
"""
|
||||||
mode = self.batch_mode
|
mode = self.batch_mode
|
||||||
data = {'mode': mode}
|
data = {'mode': mode}
|
||||||
|
phase = 1
|
||||||
|
|
||||||
schema = MobileNewReceivingBatch().bind(session=self.Session())
|
schema = MobileNewReceivingBatch().bind(session=self.Session())
|
||||||
form = forms.Form(schema=schema, request=self.request)
|
form = forms.Form(schema=schema, request=self.request)
|
||||||
if form.validate(newstyle=True):
|
if form.validate(newstyle=True):
|
||||||
|
phase = form.validated['phase']
|
||||||
|
|
||||||
if form.validated['workflow'] == 'from_scratch':
|
if form.validated['workflow'] == 'from_scratch':
|
||||||
if not self.allow_from_scratch:
|
if not self.allow_from_scratch:
|
||||||
|
@ -556,7 +560,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
batch.date_received = localtime(self.rattail_config).date()
|
batch.date_received = localtime(self.rattail_config).date()
|
||||||
kwargs = self.get_batch_kwargs(batch, mobile=True)
|
kwargs = self.get_batch_kwargs(batch, mobile=True)
|
||||||
batch = self.handler.make_batch(self.Session(), **kwargs)
|
batch = self.handler.make_batch(self.Session(), **kwargs)
|
||||||
return self.redirect(self.request.route_url('mobile.receiving.view', uuid=batch.uuid))
|
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
||||||
|
|
||||||
elif form.validated['workflow'] == 'truck_dump':
|
elif form.validated['workflow'] == 'truck_dump':
|
||||||
if not self.allow_truck_dump:
|
if not self.allow_truck_dump:
|
||||||
|
@ -565,44 +569,85 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
batch.store = self.rattail_config.get_store(self.Session())
|
batch.store = self.rattail_config.get_store(self.Session())
|
||||||
batch.mode = mode
|
batch.mode = mode
|
||||||
batch.truck_dump = True
|
batch.truck_dump = True
|
||||||
batch.vendor = self.Session.merge(form.validated['vendor'])
|
batch.vendor = self.Session.query(model.Vendor).get(form.validated['vendor'])
|
||||||
batch.created_by = self.request.user
|
batch.created_by = self.request.user
|
||||||
batch.date_received = localtime(self.rattail_config).date()
|
batch.date_received = localtime(self.rattail_config).date()
|
||||||
kwargs = self.get_batch_kwargs(batch, mobile=True)
|
kwargs = self.get_batch_kwargs(batch, mobile=True)
|
||||||
batch = self.handler.make_batch(self.Session(), **kwargs)
|
batch = self.handler.make_batch(self.Session(), **kwargs)
|
||||||
return self.redirect(self.request.route_url('mobile.receiving.view', uuid=batch.uuid))
|
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
||||||
|
|
||||||
else:
|
elif form.validated['workflow'] == 'from_po':
|
||||||
raise NotImplementedError("Requested workflow not supported: {}".format(form.validated['workflow']))
|
if not self.allow_from_po:
|
||||||
|
raise NotImplementedError("Requested workflow not supported: from_po")
|
||||||
|
|
||||||
vendor = None
|
vendor = self.Session.query(model.Vendor).get(form.validated['vendor'])
|
||||||
if self.request.method == 'POST' and self.request.POST.get('vendor'):
|
|
||||||
vendor = self.Session.query(model.Vendor).get(self.request.POST['vendor'])
|
|
||||||
if vendor:
|
|
||||||
data['vendor'] = vendor
|
data['vendor'] = vendor
|
||||||
|
|
||||||
if self.request.POST.get('purchase'):
|
schema = self.make_mobile_receiving_from_po_schema()
|
||||||
purchase = self.get_purchase(self.request.POST['purchase'])
|
po_form = forms.Form(schema=schema, request=self.request)
|
||||||
if purchase:
|
if phase == 2:
|
||||||
|
if po_form.validate(newstyle=True):
|
||||||
batch = self.model_class()
|
batch = self.model_class()
|
||||||
|
batch.store = self.rattail_config.get_store(self.Session())
|
||||||
batch.mode = mode
|
batch.mode = mode
|
||||||
batch.vendor = vendor
|
batch.vendor = vendor
|
||||||
batch.store = self.rattail_config.get_store(self.Session())
|
|
||||||
batch.buyer = self.request.user.employee
|
batch.buyer = self.request.user.employee
|
||||||
batch.created_by = self.request.user
|
batch.created_by = self.request.user
|
||||||
|
batch.date_received = localtime(self.rattail_config).date()
|
||||||
|
self.assign_purchase_order(batch, po_form)
|
||||||
kwargs = self.get_batch_kwargs(batch, mobile=True)
|
kwargs = self.get_batch_kwargs(batch, mobile=True)
|
||||||
batch = self.handler.make_batch(self.Session(), **kwargs)
|
batch = self.handler.make_batch(self.Session(), **kwargs)
|
||||||
if self.handler.should_populate(batch):
|
if self.handler.should_populate(batch):
|
||||||
self.handler.populate(batch)
|
self.handler.populate(batch)
|
||||||
return self.redirect(self.request.route_url('mobile.receiving.view', uuid=batch.uuid))
|
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
||||||
|
|
||||||
|
else:
|
||||||
|
phase = 2
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("Requested workflow not supported: {}".format(form.validated['workflow']))
|
||||||
|
|
||||||
|
data['form'] = form
|
||||||
|
data['dform'] = form.make_deform_form()
|
||||||
data['mode_title'] = self.enum.PURCHASE_BATCH_MODE[mode].capitalize()
|
data['mode_title'] = self.enum.PURCHASE_BATCH_MODE[mode].capitalize()
|
||||||
if vendor:
|
data['phase'] = phase
|
||||||
|
if phase == 2:
|
||||||
purchases = self.eligible_purchases(vendor.uuid, mode=mode)
|
purchases = self.eligible_purchases(vendor.uuid, mode=mode)
|
||||||
data['purchases'] = [(p['key'], p['display']) for p in purchases['purchases']]
|
data['purchases'] = [(p['key'], p['display']) for p in purchases['purchases']]
|
||||||
|
data['purchase_order_fieldname'] = self.purchase_order_fieldname
|
||||||
return self.render_to_response('create', data, mobile=True)
|
return self.render_to_response('create', data, mobile=True)
|
||||||
|
|
||||||
|
def make_mobile_receiving_from_po_schema(self):
|
||||||
|
schema = colander.MappingSchema()
|
||||||
|
schema.add(colander.SchemaNode(colander.String(),
|
||||||
|
name=self.purchase_order_fieldname,
|
||||||
|
validator=self.validate_purchase))
|
||||||
|
return schema.bind(session=self.Session())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@colander.deferred
|
||||||
|
def validate_purchase(node, kw):
|
||||||
|
session = kw['session']
|
||||||
|
def validate(node, value):
|
||||||
|
purchase = session.query(model.Purchase).get(value)
|
||||||
|
if not purchase:
|
||||||
|
raise colander.Invalid(node, "Purchase not found")
|
||||||
|
return purchase.uuid
|
||||||
|
return validate
|
||||||
|
|
||||||
|
def assign_purchase_order(self, batch, po_form):
|
||||||
|
"""
|
||||||
|
Assign the original purchase order to the given batch. Default
|
||||||
|
behavior assumes a Rattail Purchase object is what we're after.
|
||||||
|
"""
|
||||||
|
purchase = self.get_purchase(po_form.validated[self.purchase_order_fieldname])
|
||||||
|
if isinstance(purchase, model.Purchase):
|
||||||
|
batch.purchase = purchase
|
||||||
|
|
||||||
|
department = self.department_for_purchase(purchase)
|
||||||
|
if department:
|
||||||
|
batch.department = department
|
||||||
|
|
||||||
def configure_mobile_form(self, f):
|
def configure_mobile_form(self, f):
|
||||||
super(ReceivingBatchView, self).configure_mobile_form(f)
|
super(ReceivingBatchView, self).configure_mobile_form(f)
|
||||||
batch = f.model_instance
|
batch = f.model_instance
|
||||||
|
@ -950,6 +995,13 @@ class MobileNewReceivingBatch(colander.MappingSchema):
|
||||||
'truck_dump',
|
'truck_dump',
|
||||||
]))
|
]))
|
||||||
|
|
||||||
|
phase = colander.SchemaNode(colander.Int())
|
||||||
|
|
||||||
|
|
||||||
|
class MobileNewReceivingFromPO(colander.MappingSchema):
|
||||||
|
|
||||||
|
purchase = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
|
||||||
# TODO: this is a stopgap measure to fix an obvious bug, which exists when the
|
# TODO: this is a stopgap measure to fix an obvious bug, which exists when the
|
||||||
# session is not provided by the view at runtime (i.e. when it was instead
|
# session is not provided by the view at runtime (i.e. when it was instead
|
||||||
|
|
Loading…
Reference in a new issue