Add dialog for viewing product SRP history

only old jquery theme is supported, for now
This commit is contained in:
Lance Edgar 2020-01-02 06:55:02 -06:00
parent 8947a4d14f
commit 7dce154cc3
2 changed files with 163 additions and 17 deletions

View file

@ -1,10 +1,41 @@
## -*- coding: utf-8; -*- ## -*- coding: utf-8; -*-
<%inherit file="/master/view.mako" /> <%inherit file="/master/view.mako" />
<%def name="extra_javascript()">
${parent.extra_javascript()}
% if not use_buefy and request.rattail_config.versioning_enabled() and master.has_perm('versions'):
<script type="text/javascript">
$(function() {
$('#view-srp-history').on('click', function() {
$('#srp-history-dialog').dialog({
title: "SRP History",
width: 550,
height: 300,
modal: true,
buttons: [
{
text: "Close",
click: function() {
$(this).dialog('close');
}
}
]
});
return false;
});
});
</script>
% endif
</%def>
<%def name="extra_styles()"> <%def name="extra_styles()">
${parent.extra_styles()} ${parent.extra_styles()}
% if use_buefy:
<style type="text/css"> <style type="text/css">
% if use_buefy:
#main-product-panel { #main-product-panel {
margin-right: 2em; margin-right: 2em;
margin-top: 1em; margin-top: 1em;
@ -12,8 +43,12 @@
#pricing-panel .field-wrapper .field { #pricing-panel .field-wrapper .field {
white-space: nowrap; white-space: nowrap;
} }
</style> % else:
#srp-history-dialog .grid {
color: black;
}
% endif % endif
</style>
</%def> </%def>
<%def name="render_main_fields(form)"> <%def name="render_main_fields(form)">
@ -341,6 +376,12 @@
</div> </div>
</div> </div>
% if request.rattail_config.versioning_enabled() and master.has_perm('versions'):
<div id="srp-history-dialog" style="display: none;">
${srp_history_grid.render_grid()|n}
</div>
% endif
% endif % endif
% if buttons: % if buttons:

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2018 Lance Edgar # Copyright © 2010-2020 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -30,16 +30,19 @@ import re
import logging import logging
import six import six
import humanize
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
import sqlalchemy_continuum as continuum
from rattail import enum, pod, sil from rattail import enum, pod, sil
from rattail.db import model, api, auth, Session as RattailSession from rattail.db import model, api, auth, Session as RattailSession
from rattail.gpc import GPC from rattail.gpc import GPC
from rattail.threads import Thread from rattail.threads import Thread
from rattail.exceptions import LabelPrintingError from rattail.exceptions import LabelPrintingError
from rattail.util import load_object, pretty_quantity from rattail.util import load_object, pretty_quantity, OrderedDict
from rattail.batch import get_batch_handler from rattail.batch import get_batch_handler
from rattail.time import localtime, make_utc
import colander import colander
from deform import widget as dfwidget from deform import widget as dfwidget
@ -383,7 +386,7 @@ class ProductsView(MasterView):
f.remove_field('suggested_price') f.remove_field('suggested_price')
else: else:
f.set_readonly('suggested_price') f.set_readonly('suggested_price')
f.set_renderer('suggested_price', self.render_price) f.set_renderer('suggested_price', self.render_suggested_price)
# regular_price # regular_price
if self.creating: if self.creating:
@ -445,11 +448,11 @@ class ProductsView(MasterView):
def render_cost(self, product, field): def render_cost(self, product, field):
cost = getattr(product, field) cost = getattr(product, field)
if cost: if not cost:
if cost.unit_cost: return ""
return "$ {:0.2f}".format(cost.unit_cost) if cost.unit_cost is None:
else: return ""
return "TODO: does this item have a cost?" return "${:0.2f}".format(cost.unit_cost)
def render_price(self, product, column): def render_price(self, product, column):
price = product[column] price = product[column]
@ -470,13 +473,31 @@ class ProductsView(MasterView):
return "$ {:0.2f} / {}".format(price.pack_price, price.pack_multiple) return "$ {:0.2f} / {}".format(price.pack_price, price.pack_multiple)
return "" return ""
def render_cost(self, product, column): def add_srp_history_link(self, text):
cost = product.cost if not self.rattail_config.versioning_enabled():
if not cost: return text
return "" if not self.has_perm('versions'):
if cost.unit_cost is None: return text
return ""
return "${:0.2f}".format(cost.unit_cost) history = tags.link_to("(view history)", '#',
id='view-srp-history')
if not text:
return history
text = HTML.tag('span', c=text)
br = HTML.tag('br')
return HTML.tag('div', c=[text, br, history])
def render_suggested_price(self, product, column):
text = self.render_price(product, column)
if text and self.rattail_config.versioning_enabled():
history = self.get_srp_history(product)
if history:
date = localtime(self.rattail_config, history[0]['changed'], from_utc=True).date()
text = "{} (as of {})".format(text, date)
return self.add_srp_history_link(text)
def render_true_cost(self, product, field): def render_true_cost(self, product, field):
if not product.volatile: if not product.volatile:
@ -906,12 +927,96 @@ class ProductsView(MasterView):
if not kwargs.get('image_url'): if not kwargs.get('image_url'):
kwargs['image_url'] = self.request.static_url('tailbone:static/img/product.png') kwargs['image_url'] = self.request.static_url('tailbone:static/img/product.png')
# add SRP history, if user has access
if self.rattail_config.versioning_enabled() and self.has_perm('versions'):
data = self.get_srp_history(product)
grid = grids.Grid('products.srp_history', data,
request=self.request,
columns=[
'price',
'since',
'changed',
'changed_by',
])
grid.set_type('price', 'currency')
grid.set_type('changed', 'datetime')
kwargs['srp_history_grid'] = grid
kwargs['costs_label_preferred'] = "Pref." kwargs['costs_label_preferred'] = "Pref."
kwargs['costs_label_vendor'] = "Vendor" kwargs['costs_label_vendor'] = "Vendor"
kwargs['costs_label_code'] = "Order Code" kwargs['costs_label_code'] = "Order Code"
kwargs['costs_label_case_size'] = "Case Size" kwargs['costs_label_case_size'] = "Case Size"
return kwargs return kwargs
def get_srp_history(self, product):
"""
Returns a sequence of "records" which corresponds to the given
product's SRP history.
"""
Transaction = continuum.transaction_class(model.Product)
ProductVersion = continuum.version_class(model.Product)
ProductPriceVersion = continuum.version_class(model.ProductPrice)
now = make_utc()
history = []
# first we find all relevant ProductVersion records
versions = self.Session.query(ProductVersion)\
.join(Transaction,
Transaction.id == ProductVersion.transaction_id)\
.filter(ProductVersion.uuid == product.uuid)\
.order_by(Transaction.issued_at,
Transaction.id)\
.all()
last_uuid = None
for version in versions:
if version.suggested_price_uuid != last_uuid:
changed = version.transaction.issued_at
if version.suggested_price:
assert isinstance(version.suggested_price, ProductPriceVersion)
price = version.suggested_price.price
else:
price = None
history.append({
'transaction_id': version.transaction.id,
'price': price,
'since': humanize.naturaltime(now - changed),
'changed': changed,
'changed_by': version.transaction.user,
})
last_uuid = version.suggested_price_uuid
# next we find all relevant ProductPriceVersion records
versions = self.Session.query(ProductPriceVersion)\
.join(Transaction,
Transaction.id == ProductPriceVersion.transaction_id)\
.filter(ProductPriceVersion.product_uuid == product.uuid)\
.filter(ProductPriceVersion.type == self.enum.PRICE_TYPE_MFR_SUGGESTED)\
.order_by(Transaction.issued_at,
Transaction.id)\
.all()
last_price = None
for version in versions:
if version.price != last_price:
changed = version.transaction.issued_at
price = version.price
history.append({
'transaction_id': version.transaction.id,
'price': version.price,
'since': humanize.naturaltime(now - changed),
'changed': changed,
'changed_by': version.transaction.user,
})
last_price = version.price
final_history = OrderedDict()
for hist in reversed(history):
if hist['transaction_id'] not in final_history:
final_history[hist['transaction_id']] = hist
return list(final_history.values())
def edit(self): def edit(self):
# TODO: Should add some more/better hooks, so don't have to duplicate # TODO: Should add some more/better hooks, so don't have to duplicate
# so much code here. # so much code here.