Add basic "resolve" support for person, product from new custorder
This commit is contained in:
parent
1b0d6581db
commit
82dfce6f81
143
tailbone/templates/customers/pending/view.mako
Normal file
143
tailbone/templates/customers/pending/view.mako
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/master/view.mako" />
|
||||||
|
## <%namespace file="/util.mako" import="view_profiles_helper" />
|
||||||
|
|
||||||
|
<%def name="object_helpers()">
|
||||||
|
${parent.object_helpers()}
|
||||||
|
|
||||||
|
% if instance.custorder_records:
|
||||||
|
<nav class="panel">
|
||||||
|
<p class="panel-heading">Cross-Reference</p>
|
||||||
|
<div class="panel-block">
|
||||||
|
<div style="display: flex; flex-direction: column;">
|
||||||
|
<p class="block">
|
||||||
|
This ${model_title} is referenced by the following<br />
|
||||||
|
Customer Orders:
|
||||||
|
</p>
|
||||||
|
<ul class="list">
|
||||||
|
% for order in instance.custorder_records:
|
||||||
|
<li class="list-item">
|
||||||
|
${h.link_to(order, url('custorders.view', uuid=order.uuid))}
|
||||||
|
</li>
|
||||||
|
% endfor
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
## % if instance.status_code == enum.PENDING_CUSTOMER_STATUS_PENDING and master.has_any_perm('resolve_person', 'resolve_customer'):
|
||||||
|
% if instance.status_code == enum.PENDING_CUSTOMER_STATUS_PENDING and master.has_perm('resolve_person'):
|
||||||
|
<nav class="panel">
|
||||||
|
<p class="panel-heading">Tools</p>
|
||||||
|
<div class="panel-block">
|
||||||
|
<div style="display: flex; flex-direction: column;">
|
||||||
|
% if master.has_perm('resolve_person'):
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button type="is-primary"
|
||||||
|
@click="resolvePersonInit()"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="object-ungroup">
|
||||||
|
Resolve Person
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
## % if master.has_perm('resolve_customer'):
|
||||||
|
## <div class="buttons">
|
||||||
|
## <b-button type="is-primary"
|
||||||
|
## icon-pack="fas"
|
||||||
|
## icon-left="object-ungroup">
|
||||||
|
## Resolve Customer
|
||||||
|
## </b-button>
|
||||||
|
## </div>
|
||||||
|
## % endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<b-modal has-modal-card
|
||||||
|
:active.sync="resolvePersonShowDialog">
|
||||||
|
<div class="modal-card">
|
||||||
|
${h.form(url('{}.resolve_person'.format(route_prefix), uuid=instance.uuid), ref='resolvePersonForm')}
|
||||||
|
${h.csrf_token(request)}
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Resolve Person</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<p class="block">
|
||||||
|
If this Person already exists, you can declare that by
|
||||||
|
identifying the record below.
|
||||||
|
</p>
|
||||||
|
<p class="block">
|
||||||
|
The app will take care of updating any Customer Orders
|
||||||
|
etc. as needed once you declare the match.
|
||||||
|
</p>
|
||||||
|
<b-field grouped>
|
||||||
|
<b-field label="Pending">
|
||||||
|
<span>${instance.display_name}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field label="Actual Person" expanded>
|
||||||
|
<tailbone-autocomplete name="person_uuid"
|
||||||
|
v-model="resolvePersonUUID"
|
||||||
|
ref="resolvePersonAutocomplete"
|
||||||
|
service-url="${url('people.autocomplete')}">
|
||||||
|
</tailbone-autocomplete>
|
||||||
|
</b-field>
|
||||||
|
</b-field>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<b-button @click="resolvePersonShowDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
:disabled="resolvePersonSubmitDisabled"
|
||||||
|
@click="resolvePersonSubmit()"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="object-ungroup">
|
||||||
|
{{ resolvePersonSubmitting ? "Working, please wait..." : "I declare these are the same" }}
|
||||||
|
</b-button>
|
||||||
|
</footer>
|
||||||
|
${h.end_form()}
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="modify_this_page_vars()">
|
||||||
|
${parent.modify_this_page_vars()}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
ThisPageData.resolvePersonShowDialog = false
|
||||||
|
ThisPageData.resolvePersonUUID = null
|
||||||
|
ThisPageData.resolvePersonSubmitting = false
|
||||||
|
|
||||||
|
ThisPage.computed.resolvePersonSubmitDisabled = function() {
|
||||||
|
if (this.resolvePersonSubmitting) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (!this.resolvePersonUUID) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPage.methods.resolvePersonInit = function() {
|
||||||
|
this.resolvePersonUUID = null
|
||||||
|
this.resolvePersonShowDialog = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.resolvePersonAutocomplete.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPage.methods.resolvePersonSubmit = function() {
|
||||||
|
this.resolvePersonSubmitting = true
|
||||||
|
this.$refs.resolvePersonForm.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
|
@ -12,18 +12,6 @@
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_instance_header_buttons()">
|
|
||||||
${parent.render_instance_header_buttons()}
|
|
||||||
% if use_buefy and master.configurable and master.has_perm('configure'):
|
|
||||||
<div class="level-item">
|
|
||||||
<once-button tag="a" href="${url('{}.configure'.format(route_prefix))}"
|
|
||||||
icon-left="cog"
|
|
||||||
text="Configure">
|
|
||||||
</once-button>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="page_content()">
|
<%def name="page_content()">
|
||||||
<br />
|
<br />
|
||||||
% if use_buefy:
|
% if use_buefy:
|
||||||
|
@ -1968,6 +1956,9 @@
|
||||||
% if product_price_may_be_questionable:
|
% if product_price_may_be_questionable:
|
||||||
this.productPriceNeedsConfirmation = false
|
this.productPriceNeedsConfirmation = false
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
|
this.itemDialogTabIndex = 1
|
||||||
|
|
||||||
}, response => {
|
}, response => {
|
||||||
this.clearProduct()
|
this.clearProduct()
|
||||||
})
|
})
|
||||||
|
|
130
tailbone/templates/products/pending/view.mako
Normal file
130
tailbone/templates/products/pending/view.mako
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/master/view.mako" />
|
||||||
|
|
||||||
|
<%def name="object_helpers()">
|
||||||
|
${parent.object_helpers()}
|
||||||
|
% if instance.custorder_item_records:
|
||||||
|
<nav class="panel">
|
||||||
|
<p class="panel-heading">Cross-Reference</p>
|
||||||
|
<div class="panel-block">
|
||||||
|
<div style="display: flex; flex-direction: column;">
|
||||||
|
<p class="block">
|
||||||
|
This ${model_title} is referenced by the following<br />
|
||||||
|
Customer Order Items:
|
||||||
|
</p>
|
||||||
|
<ul class="list">
|
||||||
|
% for item in instance.custorder_item_records:
|
||||||
|
<li class="list-item">
|
||||||
|
${h.link_to('#{}-{}'.format(item.order.id, item.sequence), url('custorders.items.view', uuid=item.uuid))}
|
||||||
|
</li>
|
||||||
|
% endfor
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if instance.status_code == enum.PENDING_PRODUCT_STATUS_PENDING and master.has_perm('resolve_product'):
|
||||||
|
<nav class="panel">
|
||||||
|
<p class="panel-heading">Tools</p>
|
||||||
|
<div class="panel-block">
|
||||||
|
<div style="display: flex; flex-direction: column;">
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button type="is-primary"
|
||||||
|
@click="resolveProductInit()"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="object-ungroup">
|
||||||
|
Resolve Product
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<b-modal has-modal-card
|
||||||
|
:active.sync="resolveProductShowDialog">
|
||||||
|
<div class="modal-card">
|
||||||
|
${h.form(url('{}.resolve_product'.format(route_prefix), uuid=instance.uuid), ref='resolveProductForm')}
|
||||||
|
${h.csrf_token(request)}
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Resolve Product</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<p class="block">
|
||||||
|
If this Product already exists, you can declare that by
|
||||||
|
identifying the record below.
|
||||||
|
</p>
|
||||||
|
<p class="block">
|
||||||
|
The app will take care of updating any Customer Orders
|
||||||
|
etc. as needed once you declare the match.
|
||||||
|
</p>
|
||||||
|
<b-field grouped>
|
||||||
|
<b-field label="Pending">
|
||||||
|
<span>${instance.full_description}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field label="Actual Product" expanded>
|
||||||
|
<tailbone-autocomplete name="product_uuid"
|
||||||
|
v-model="resolveProductUUID"
|
||||||
|
ref="resolveProductAutocomplete"
|
||||||
|
service-url="${url('products.autocomplete')}">
|
||||||
|
</tailbone-autocomplete>
|
||||||
|
</b-field>
|
||||||
|
</b-field>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<b-button @click="resolveProductShowDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
:disabled="resolveProductSubmitDisabled"
|
||||||
|
@click="resolveProductSubmit()"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="object-ungroup">
|
||||||
|
{{ resolveProductSubmitting ? "Working, please wait..." : "I declare these are the same" }}
|
||||||
|
</b-button>
|
||||||
|
</footer>
|
||||||
|
${h.end_form()}
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="modify_this_page_vars()">
|
||||||
|
${parent.modify_this_page_vars()}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
ThisPageData.resolveProductShowDialog = false
|
||||||
|
ThisPageData.resolveProductUUID = null
|
||||||
|
ThisPageData.resolveProductSubmitting = false
|
||||||
|
|
||||||
|
ThisPage.computed.resolveProductSubmitDisabled = function() {
|
||||||
|
if (this.resolveProductSubmitting) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (!this.resolveProductUUID) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPage.methods.resolveProductInit = function() {
|
||||||
|
this.resolveProductUUID = null
|
||||||
|
this.resolveProductShowDialog = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.resolveProductAutocomplete.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisPage.methods.resolveProductSubmit = function() {
|
||||||
|
this.resolveProductSubmitting = true
|
||||||
|
this.$refs.resolveProductForm.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${parent.body()}
|
|
@ -500,6 +500,9 @@ class PendingCustomerView(MasterView):
|
||||||
super(PendingCustomerView, self).configure_grid(g)
|
super(PendingCustomerView, self).configure_grid(g)
|
||||||
|
|
||||||
g.set_enum('status_code', self.enum.PENDING_CUSTOMER_STATUS)
|
g.set_enum('status_code', self.enum.PENDING_CUSTOMER_STATUS)
|
||||||
|
g.filters['status_code'].default_active = True
|
||||||
|
g.filters['status_code'].default_verb = 'not_equal'
|
||||||
|
g.filters['status_code'].default_value = six.text_type(self.enum.PENDING_CUSTOMER_STATUS_RESOLVED)
|
||||||
|
|
||||||
g.set_sort_defaults('display_name')
|
g.set_sort_defaults('display_name')
|
||||||
g.set_link('id')
|
g.set_link('id')
|
||||||
|
@ -523,6 +526,51 @@ class PendingCustomerView(MasterView):
|
||||||
f.set_readonly('user')
|
f.set_readonly('user')
|
||||||
f.set_renderer('user', self.render_user)
|
f.set_renderer('user', self.render_user)
|
||||||
|
|
||||||
|
def editable_instance(self, pending):
|
||||||
|
if pending.status_code == self.enum.PENDING_CUSTOMER_STATUS_RESOLVED:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def resolve_person(self):
|
||||||
|
model = self.model
|
||||||
|
pending = self.get_instance()
|
||||||
|
redirect = self.redirect(self.get_action_url('view', pending))
|
||||||
|
|
||||||
|
uuid = self.request.POST['person_uuid']
|
||||||
|
person = self.Session.query(model.Person).get(uuid)
|
||||||
|
if not person:
|
||||||
|
self.request.session.flash("Person not found!", 'error')
|
||||||
|
return redirect
|
||||||
|
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
people_handler = app.get_people_handler()
|
||||||
|
people_handler.resolve_person(pending, person, self.request.user)
|
||||||
|
self.Session.flush()
|
||||||
|
return redirect
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def defaults(cls, config):
|
||||||
|
cls._defaults(config)
|
||||||
|
cls._pending_customer_defaults(config)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _pending_customer_defaults(cls, config):
|
||||||
|
route_prefix = cls.get_route_prefix()
|
||||||
|
instance_url_prefix = cls.get_instance_url_prefix()
|
||||||
|
permission_prefix = cls.get_permission_prefix()
|
||||||
|
model_title = cls.get_model_title()
|
||||||
|
|
||||||
|
# resolve person
|
||||||
|
config.add_tailbone_permission(permission_prefix,
|
||||||
|
'{}.resolve_person'.format(permission_prefix),
|
||||||
|
"Resolve a {} as a Person".format(model_title))
|
||||||
|
config.add_route('{}.resolve_person'.format(route_prefix),
|
||||||
|
'{}/resolve-person'.format(instance_url_prefix),
|
||||||
|
request_method='POST')
|
||||||
|
config.add_view(cls, attr='resolve_person',
|
||||||
|
route_name='{}.resolve_person'.format(route_prefix),
|
||||||
|
permission='{}.resolve_person'.format(permission_prefix))
|
||||||
|
|
||||||
|
|
||||||
# # TODO: this is referenced by some custom apps, but should be moved??
|
# # TODO: this is referenced by some custom apps, but should be moved??
|
||||||
# def unique_id(value, field):
|
# def unique_id(value, field):
|
||||||
|
|
|
@ -52,6 +52,7 @@ class CustomerOrderItemView(MasterView):
|
||||||
deletable = False
|
deletable = False
|
||||||
|
|
||||||
labels = {
|
labels = {
|
||||||
|
'order': "Customer Order",
|
||||||
'order_id': "Order ID",
|
'order_id': "Order ID",
|
||||||
'order_uom': "Order UOM",
|
'order_uom': "Order UOM",
|
||||||
'status_code': "Status",
|
'status_code': "Status",
|
||||||
|
@ -172,21 +173,37 @@ class CustomerOrderItemView(MasterView):
|
||||||
def configure_form(self, f):
|
def configure_form(self, f):
|
||||||
super(CustomerOrderItemView, self).configure_form(f)
|
super(CustomerOrderItemView, self).configure_form(f)
|
||||||
use_buefy = self.get_use_buefy()
|
use_buefy = self.get_use_buefy()
|
||||||
|
item = f.model_instance
|
||||||
|
|
||||||
# order
|
# order
|
||||||
f.set_renderer('order', self.render_order)
|
f.set_renderer('order', self.render_order)
|
||||||
|
|
||||||
# product
|
# (pending) product
|
||||||
f.set_renderer('product', self.render_product)
|
f.set_renderer('product', self.render_product)
|
||||||
|
|
||||||
# pending_product
|
|
||||||
f.set_renderer('pending_product', self.render_pending_product)
|
f.set_renderer('pending_product', self.render_pending_product)
|
||||||
|
if self.viewing:
|
||||||
|
if item.product and not item.pending_product:
|
||||||
|
f.remove('pending_product')
|
||||||
|
elif item.pending_product and not item.product:
|
||||||
|
f.remove('product')
|
||||||
|
|
||||||
# product uom
|
# product uom
|
||||||
f.set_enum('product_unit_of_measure', self.enum.UNIT_OF_MEASURE)
|
f.set_enum('product_unit_of_measure', self.enum.UNIT_OF_MEASURE)
|
||||||
|
|
||||||
|
# highlight pending fields
|
||||||
|
f.set_renderer('product_brand', self.highlight_pending_field)
|
||||||
|
f.set_renderer('product_description', self.highlight_pending_field)
|
||||||
|
f.set_renderer('product_size', self.highlight_pending_field)
|
||||||
|
f.set_renderer('case_quantity', self.highlight_pending_field_quantity)
|
||||||
|
|
||||||
|
'unit_price',
|
||||||
|
'total_price',
|
||||||
|
'price_needs_confirmation',
|
||||||
|
'paid_amount',
|
||||||
|
'status_code',
|
||||||
|
'notes',
|
||||||
|
|
||||||
# quantity fields
|
# quantity fields
|
||||||
f.set_type('case_quantity', 'quantity')
|
|
||||||
f.set_type('cases_ordered', 'quantity')
|
f.set_type('cases_ordered', 'quantity')
|
||||||
f.set_type('units_ordered', 'quantity')
|
f.set_type('units_ordered', 'quantity')
|
||||||
f.set_type('order_quantity', 'quantity')
|
f.set_type('order_quantity', 'quantity')
|
||||||
|
@ -210,10 +227,27 @@ class CustomerOrderItemView(MasterView):
|
||||||
else:
|
else:
|
||||||
f.remove('notes')
|
f.remove('notes')
|
||||||
|
|
||||||
|
def highlight_pending_field(self, item, field, value=None):
|
||||||
|
if value is None:
|
||||||
|
value = getattr(item, field)
|
||||||
|
if not item.product_uuid and item.pending_product_uuid:
|
||||||
|
return HTML.tag('span', c=[value],
|
||||||
|
class_='has-text-success')
|
||||||
|
return value
|
||||||
|
|
||||||
|
def highlight_pending_field_quantity(self, item, field):
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
value = getattr(item, field)
|
||||||
|
value = app.render_quantity(value)
|
||||||
|
return self.highlight_pending_field(item, field, value)
|
||||||
|
|
||||||
def render_price_with_confirmation(self, item, field):
|
def render_price_with_confirmation(self, item, field):
|
||||||
price = getattr(item, field)
|
price = getattr(item, field)
|
||||||
app = self.get_rattail_app()
|
app = self.get_rattail_app()
|
||||||
text = app.render_currency(price)
|
text = app.render_currency(price)
|
||||||
|
if not item.product_uuid and item.pending_product_uuid:
|
||||||
|
text = HTML.tag('span', c=[text],
|
||||||
|
class_='has-text-success')
|
||||||
if item.price_needs_confirmation:
|
if item.price_needs_confirmation:
|
||||||
return HTML.tag('span', class_='has-background-warning',
|
return HTML.tag('span', class_='has-background-warning',
|
||||||
c=[text])
|
c=[text])
|
||||||
|
|
|
@ -108,21 +108,24 @@ class CustomerOrderView(MasterView):
|
||||||
def configure_grid(self, g):
|
def configure_grid(self, g):
|
||||||
super(CustomerOrderView, self).configure_grid(g)
|
super(CustomerOrderView, self).configure_grid(g)
|
||||||
|
|
||||||
|
# customer or person
|
||||||
|
if self.batch_handler.new_order_requires_customer():
|
||||||
|
g.remove('person')
|
||||||
g.set_joiner('customer', lambda q: q.outerjoin(model.Customer))
|
g.set_joiner('customer', lambda q: q.outerjoin(model.Customer))
|
||||||
g.set_joiner('person', lambda q: q.outerjoin(model.Person))
|
g.set_sorter('customer', model.Customer.name)
|
||||||
|
|
||||||
g.filters['customer'] = g.make_filter('customer', model.Customer.name,
|
g.filters['customer'] = g.make_filter('customer', model.Customer.name,
|
||||||
label="Customer Name",
|
label="Customer Name",
|
||||||
default_active=True,
|
default_active=True,
|
||||||
default_verb='contains')
|
default_verb='contains')
|
||||||
|
else:
|
||||||
|
g.remove('customer')
|
||||||
|
g.set_joiner('person', lambda q: q.outerjoin(model.Person))
|
||||||
|
g.set_sorter('person', model.Person.display_name)
|
||||||
g.filters['person'] = g.make_filter('person', model.Person.display_name,
|
g.filters['person'] = g.make_filter('person', model.Person.display_name,
|
||||||
label="Person Name",
|
label="Person Name",
|
||||||
default_active=True,
|
default_active=True,
|
||||||
default_verb='contains')
|
default_verb='contains')
|
||||||
|
|
||||||
g.set_sorter('customer', model.Customer.name)
|
|
||||||
g.set_sorter('person', model.Person.display_name)
|
|
||||||
|
|
||||||
g.set_enum('status_code', self.enum.CUSTORDER_STATUS)
|
g.set_enum('status_code', self.enum.CUSTORDER_STATUS)
|
||||||
|
|
||||||
g.set_sort_defaults('created', 'desc')
|
g.set_sort_defaults('created', 'desc')
|
||||||
|
@ -133,13 +136,33 @@ class CustomerOrderView(MasterView):
|
||||||
|
|
||||||
def configure_form(self, f):
|
def configure_form(self, f):
|
||||||
super(CustomerOrderView, self).configure_form(f)
|
super(CustomerOrderView, self).configure_form(f)
|
||||||
|
order = f.model_instance
|
||||||
|
|
||||||
f.set_readonly('id')
|
f.set_readonly('id')
|
||||||
|
|
||||||
f.set_renderer('store', self.render_store)
|
f.set_renderer('store', self.render_store)
|
||||||
|
|
||||||
|
# (pending) customer
|
||||||
f.set_renderer('customer', self.render_customer)
|
f.set_renderer('customer', self.render_customer)
|
||||||
f.set_renderer('person', self.render_person)
|
f.set_renderer('person', self.render_person)
|
||||||
f.set_renderer('pending_customer', self.render_pending_customer)
|
f.set_renderer('pending_customer', self.render_pending_customer)
|
||||||
|
if self.viewing:
|
||||||
|
if self.batch_handler.new_order_requires_customer():
|
||||||
|
f.remove('person')
|
||||||
|
if order.customer and not order.pending_customer:
|
||||||
|
f.remove('pending_customer')
|
||||||
|
elif order.pending_customer and not order.customer:
|
||||||
|
f.remove('customer')
|
||||||
|
else:
|
||||||
|
f.remove('customer')
|
||||||
|
if order.person and not order.pending_customer:
|
||||||
|
f.remove('pending_customer')
|
||||||
|
elif order.pending_customer and not order.person:
|
||||||
|
f.remove('person')
|
||||||
|
|
||||||
|
# contact info
|
||||||
|
f.set_renderer('phone_number', self.highlight_pending_field)
|
||||||
|
f.set_renderer('email_address', self.highlight_pending_field)
|
||||||
|
|
||||||
f.set_type('total_price', 'currency')
|
f.set_type('total_price', 'currency')
|
||||||
|
|
||||||
|
@ -150,6 +173,20 @@ class CustomerOrderView(MasterView):
|
||||||
f.set_readonly('created_by')
|
f.set_readonly('created_by')
|
||||||
f.set_renderer('created_by', self.render_user)
|
f.set_renderer('created_by', self.render_user)
|
||||||
|
|
||||||
|
def highlight_pending_field(self, order, field):
|
||||||
|
value = getattr(order, field)
|
||||||
|
pending = False
|
||||||
|
if self.batch_handler.new_order_requires_customer():
|
||||||
|
if not order.customer_uuid and order.pending_customer_uuid:
|
||||||
|
pending = True
|
||||||
|
else:
|
||||||
|
if not order.person_uuid and order.pending_customer_uuid:
|
||||||
|
pending = True
|
||||||
|
if pending:
|
||||||
|
return HTML.tag('span', c=[value],
|
||||||
|
class_='has-text-success')
|
||||||
|
return value
|
||||||
|
|
||||||
def render_person(self, order, field):
|
def render_person(self, order, field):
|
||||||
person = order.person
|
person = order.person
|
||||||
if not person:
|
if not person:
|
||||||
|
@ -164,7 +201,8 @@ class CustomerOrderView(MasterView):
|
||||||
return
|
return
|
||||||
text = six.text_type(pending)
|
text = six.text_type(pending)
|
||||||
url = self.request.route_url('pending_customers.view', uuid=pending.uuid)
|
url = self.request.route_url('pending_customers.view', uuid=pending.uuid)
|
||||||
return tags.link_to(text, url)
|
return tags.link_to(text, url,
|
||||||
|
class_='has-background-warning')
|
||||||
|
|
||||||
def get_row_data(self, order):
|
def get_row_data(self, order):
|
||||||
return self.Session.query(model.CustomerOrderItem)\
|
return self.Session.query(model.CustomerOrderItem)\
|
||||||
|
@ -218,6 +256,10 @@ class CustomerOrderView(MasterView):
|
||||||
g.set_link('product_brand')
|
g.set_link('product_brand')
|
||||||
g.set_link('product_description')
|
g.set_link('product_description')
|
||||||
|
|
||||||
|
def row_grid_extra_class(self, item, i):
|
||||||
|
if not item.product_uuid and item.pending_product_uuid:
|
||||||
|
return 'has-text-success'
|
||||||
|
|
||||||
def render_price_with_confirmation(self, item, field):
|
def render_price_with_confirmation(self, item, field):
|
||||||
price = getattr(item, field)
|
price = getattr(item, field)
|
||||||
app = self.get_rattail_app()
|
app = self.get_rattail_app()
|
||||||
|
|
|
@ -290,6 +290,12 @@ class MasterView(View):
|
||||||
return self.request.has_perm('{}.{}'.format(
|
return self.request.has_perm('{}.{}'.format(
|
||||||
self.get_permission_prefix(), name))
|
self.get_permission_prefix(), name))
|
||||||
|
|
||||||
|
def has_any_perm(self, *names):
|
||||||
|
for name in names:
|
||||||
|
if self.has_perm(name):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_config_url(cls):
|
def get_config_url(cls):
|
||||||
if hasattr(cls, 'config_url'):
|
if hasattr(cls, 'config_url'):
|
||||||
|
@ -801,7 +807,8 @@ class MasterView(View):
|
||||||
return
|
return
|
||||||
text = six.text_type(pending)
|
text = six.text_type(pending)
|
||||||
url = self.request.route_url('pending_products.view', uuid=pending.uuid)
|
url = self.request.route_url('pending_products.view', uuid=pending.uuid)
|
||||||
return tags.link_to(text, url)
|
return tags.link_to(text, url,
|
||||||
|
class_='has-background-warning')
|
||||||
|
|
||||||
def render_vendor(self, obj, field):
|
def render_vendor(self, obj, field):
|
||||||
vendor = getattr(obj, field)
|
vendor = getattr(obj, field)
|
||||||
|
|
|
@ -2035,6 +2035,9 @@ class PendingProductView(MasterView):
|
||||||
super(PendingProductView, self).configure_grid(g)
|
super(PendingProductView, self).configure_grid(g)
|
||||||
|
|
||||||
g.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS)
|
g.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS)
|
||||||
|
g.filters['status_code'].default_active = True
|
||||||
|
g.filters['status_code'].default_verb = 'not_equal'
|
||||||
|
g.filters['status_code'].default_value = six.text_type(self.enum.PENDING_PRODUCT_STATUS_RESOLVED)
|
||||||
|
|
||||||
g.set_sort_defaults('created', 'desc')
|
g.set_sort_defaults('created', 'desc')
|
||||||
|
|
||||||
|
@ -2137,6 +2140,11 @@ class PendingProductView(MasterView):
|
||||||
# f.set_readonly('status_code')
|
# f.set_readonly('status_code')
|
||||||
f.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS)
|
f.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS)
|
||||||
|
|
||||||
|
def editable_instance(self, pending):
|
||||||
|
if pending.status_code == self.enum.PENDING_PRODUCT_STATUS_RESOLVED:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def objectify(self, form, data=None):
|
def objectify(self, form, data=None):
|
||||||
if data is None:
|
if data is None:
|
||||||
data = form.validated
|
data = form.validated
|
||||||
|
@ -2182,6 +2190,45 @@ class PendingProductView(MasterView):
|
||||||
'error')
|
'error')
|
||||||
return self.redirect(self.get_action_url('view', pending))
|
return self.redirect(self.get_action_url('view', pending))
|
||||||
|
|
||||||
|
def resolve_product(self):
|
||||||
|
model = self.model
|
||||||
|
pending = self.get_instance()
|
||||||
|
redirect = self.redirect(self.get_action_url('view', pending))
|
||||||
|
|
||||||
|
uuid = self.request.POST['product_uuid']
|
||||||
|
product = self.Session.query(model.Product).get(uuid)
|
||||||
|
if not product:
|
||||||
|
self.request.session.flash("Product not found!", 'error')
|
||||||
|
return redirect
|
||||||
|
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
products_handler = app.get_products_handler()
|
||||||
|
products_handler.resolve_product(pending, product, self.request.user)
|
||||||
|
return redirect
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def defaults(cls, config):
|
||||||
|
cls._defaults(config)
|
||||||
|
cls._pending_product_defaults(config)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _pending_product_defaults(cls, config):
|
||||||
|
route_prefix = cls.get_route_prefix()
|
||||||
|
instance_url_prefix = cls.get_instance_url_prefix()
|
||||||
|
permission_prefix = cls.get_permission_prefix()
|
||||||
|
model_title = cls.get_model_title()
|
||||||
|
|
||||||
|
# resolve product
|
||||||
|
config.add_tailbone_permission(permission_prefix,
|
||||||
|
'{}.resolve_product'.format(permission_prefix),
|
||||||
|
"Resolve a {} as a Product".format(model_title))
|
||||||
|
config.add_route('{}.resolve_product'.format(route_prefix),
|
||||||
|
'{}/resolve-product'.format(instance_url_prefix),
|
||||||
|
request_method='POST')
|
||||||
|
config.add_view(cls, attr='resolve_product',
|
||||||
|
route_name='{}.resolve_product'.format(route_prefix),
|
||||||
|
permission='{}.resolve_product'.format(permission_prefix))
|
||||||
|
|
||||||
|
|
||||||
def print_labels(request):
|
def print_labels(request):
|
||||||
profile = request.params.get('profile')
|
profile = request.params.get('profile')
|
||||||
|
|
Loading…
Reference in a new issue