From e7871380a9454521926fb4386ec41fb61d5e210a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 9 Nov 2021 15:49:42 -0600 Subject: [PATCH 0001/1189] Add "true margin" to products XLSX export --- tailbone/views/products.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tailbone/views/products.py b/tailbone/views/products.py index b1feecc3..b2cb835e 100644 --- a/tailbone/views/products.py +++ b/tailbone/views/products.py @@ -663,6 +663,7 @@ class ProductView(MasterView): fields.append('vendor_name') fields.append('vendor_item_code') fields.append('unit_cost') + fields.append('true_margin') return fields @@ -724,6 +725,11 @@ class ProductView(MasterView): if 'unit_cost' in fields: row['unit_cost'] = product.cost.unit_cost if product.cost else None + if 'true_margin' in fields: + row['true_margin'] = None + if product.volatile and product.volatile.true_margin: + row['true_margin'] = product.volatile.true_margin + return row def get_instance(self): From 7630f504b00bd7ca498f2eb4d58d55cadf1b3f5b Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 9 Nov 2021 17:20:53 -0600 Subject: [PATCH 0002/1189] Add initial VersionMasterView for those times when you just need to expose a version table directly --- tailbone/views/versions.py | 89 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tailbone/views/versions.py diff --git a/tailbone/views/versions.py b/tailbone/views/versions.py new file mode 100644 index 00000000..6c370996 --- /dev/null +++ b/tailbone/views/versions.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# Rattail -- Retail Software Framework +# Copyright © 2010-2021 Lance Edgar +# +# This file is part of Rattail. +# +# Rattail is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# Rattail. If not, see . +# +################################################################################ +""" +Master view for version tables +""" + +from __future__ import unicode_literals, absolute_import + +import sqlalchemy_continuum as continuum + +from tailbone.views import MasterView +from tailbone.util import raw_datetime + + +class VersionMasterView(MasterView): + """ + Base class for version master views + """ + creatable = False + editable = False + deletable = False + + labels = { + 'transaction_issued_at': "Changed", + 'transaction_user': "Changed by", + 'transaction_id': "Transaction ID", + } + + grid_columns = [ + 'transaction_issued_at', + 'transaction_user', + 'version_parent', + 'transaction_id', + ] + + def query(self, session): + Transaction = continuum.transaction_class(self.true_model_class) + + query = session.query(self.model_class)\ + .join(Transaction, + Transaction.id == self.model_class.transaction_id) + + return query + + def configure_grid(self, g): + super(VersionMasterView, self).configure_grid(g) + Transaction = continuum.transaction_class(self.true_model_class) + + g.set_sorter('transaction_issued_at', Transaction.issued_at) + g.set_sorter('transaction_id', Transaction.id) + g.set_sort_defaults('transaction_issued_at', 'desc') + + g.set_renderer('transaction_issued_at', self.render_transaction_issued_at) + g.set_renderer('transaction_user', self.render_transaction_user) + g.set_renderer('transaction_id', self.render_transaction_id) + + g.set_link('transaction_issued_at') + g.set_link('transaction_user') + g.set_link('version_parent') + + def render_transaction_issued_at(self, version, field): + value = version.transaction.issued_at + return raw_datetime(self.rattail_config, value) + + def render_transaction_user(self, version, field): + return version.transaction.user + + def render_transaction_id(self, version, field): + return version.transaction.id From 5f9d311cdb24f3517cad30531ae1d70aede58836 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 10 Nov 2021 12:39:51 -0600 Subject: [PATCH 0003/1189] Add views for PendingProduct model; also DepartmentWidget --- tailbone/forms/widgets.py | 30 ++++++- tailbone/views/master.py | 4 +- tailbone/views/products.py | 160 +++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 2 deletions(-) diff --git a/tailbone/forms/widgets.py b/tailbone/forms/widgets.py index d8976337..ad9d9c31 100644 --- a/tailbone/forms/widgets.py +++ b/tailbone/forms/widgets.py @@ -2,7 +2,7 @@ ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2019 Lance Edgar +# Copyright © 2010-2021 Lance Edgar # # This file is part of Rattail. # @@ -36,6 +36,7 @@ import colander from deform import widget as dfwidget from webhelpers2.html import tags, HTML +from tailbone.db import Session from tailbone.forms.types import ProductQuantity @@ -278,3 +279,30 @@ class JQueryAutocompleteWidget(dfwidget.AutocompleteInputWidget): tmpl_values = self.get_template_values(field, cstruct, kw) template = readonly and self.readonly_template or self.template return field.renderer(template, **tmpl_values) + + +class DepartmentWidget(dfwidget.SelectWidget): + """ + Custom select widget for a Department reference field. + + Constructor accepts the normal ``values`` kwarg but if not + provided then the widget will fetch department list from Rattail + DB. + + Constructor also accepts ``required`` kwarg, which defaults to + true unless specified. + """ + + def __init__(self, request, **kwargs): + + if 'values' not in kwargs: + model = request.rattail_config.get_model() + departments = Session.query(model.Department)\ + .order_by(model.Department.number) + values = [(dept.uuid, six.text_type(dept)) + for dept in departments] + if not kwargs.pop('required', True): + values.insert(0, ('', "(none)")) + kwargs['values'] = values + + super(DepartmentWidget, self).__init__(**kwargs) diff --git a/tailbone/views/master.py b/tailbone/views/master.py index 1eb7686a..f288ec34 100644 --- a/tailbone/views/master.py +++ b/tailbone/views/master.py @@ -739,9 +739,11 @@ class MasterView(View): if obj.emails: return obj.emails[0].address - def render_product_key_value(self, obj): + def render_product_key_value(self, obj, field=None): """ Render the "canonical" product key value for the given object. + + nb. the ``field`` kwarg is ignored if present """ product_key = self.rattail_config.product_key() if product_key == 'upc': diff --git a/tailbone/views/products.py b/tailbone/views/products.py index b2cb835e..351dd832 100644 --- a/tailbone/views/products.py +++ b/tailbone/views/products.py @@ -1885,6 +1885,165 @@ class ProductView(MasterView): permission='{}.versions'.format(permission_prefix)) +class PendingProductView(MasterView): + """ + Master view for the Pending Product class. + """ + model_class = model.PendingProduct + route_prefix = 'pending_products' + url_prefix = '/products/pending' + + labels = { + 'regular_price_amount': "Regular Price", + 'status_code': "Status", + 'user': "Created by", + } + + grid_columns = [ + '_product_key_', + 'department_name', + 'brand_name', + 'description', + 'size', + 'created', + 'user', + 'status_code', + ] + + form_fields = [ + '_product_key_', + 'department_name', + 'department', + 'brand_name', + 'brand', + 'description', + 'size', + 'case_size', + 'regular_price_amount', + 'special_order', + 'notes', + 'created', + 'user', + 'status_code', + ] + + 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') + + g.set_link('description') + + def configure_form(self, f): + super(PendingProductView, self).configure_form(f) + 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: + f.remove('department_name') + f.replace('department', 'department_uuid') + f.set_widget('department_uuid', forms.widgets.DepartmentWidget(self.request, required=False)) + f.set_label('department_uuid', "Department") + else: + f.set_renderer('department', self.render_department) + if pending.department: + f.remove('department_name') + + # brand + if self.creating or self.editing: + f.remove('brand_name') + f.replace('brand', 'brand_uuid') + f.set_label('brand_uuid', "Brand") + + f.set_node('brand_uuid', colander.String(), missing=colander.null) + brand_display = "" + if self.request.method == 'POST': + if self.request.POST.get('brand_uuid'): + brand = self.Session.query(model.Brand).get(self.request.POST['brand_uuid']) + if brand: + brand_display = six.text_type(brand) + elif self.editing: + brand_display = six.text_type(pending.brand or '') + brands_url = self.request.route_url('brands.autocomplete') + f.set_widget('brand_uuid', forms.widgets.JQueryAutocompleteWidget( + field_display=brand_display, service_url=brands_url)) + else: + f.set_renderer('brand', self.render_brand) + if pending.brand: + f.remove('brand_name') + + # description + f.set_required('description') + + # case_size + f.set_type('case_size', 'quantity') + + # regular_price_amount + f.set_type('regular_price_amount', 'currency') + + # notes + f.set_type('notes', 'text') + + # 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) + + # status_code + if self.creating: + f.remove('status_code') + else: + # f.set_readonly('status_code') + f.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS) + + def objectify(self, form, data=None): + if data is None: + data = form.validated + + pending = super(PendingProductView, self).objectify(form, data) + + if not pending.user: + pending.user = self.request.user + + self.Session.add(pending) + self.Session.flush() + self.Session.refresh(pending) + + if pending.department: + pending.department_name = pending.department.name + + if pending.brand: + pending.brand_name = pending.brand.name + + return pending + + def print_labels(request): profile = request.params.get('profile') profile = Session.query(model.LabelProfile).get(profile) if profile else None @@ -1920,3 +2079,4 @@ def includeme(config): renderer='json', permission='products.print_labels') ProductView.defaults(config) + PendingProductView.defaults(config) From 1ceb1e4434f1246fa3624043965563c28caf3fcf Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 11 Nov 2021 12:11:24 -0600 Subject: [PATCH 0004/1189] Update changelog --- CHANGES.rst | 10 ++++++++++ tailbone/_version.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 622d0c0f..a579ad70 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,16 @@ CHANGELOG ========= +0.8.171 (2021-11-11) +-------------------- + +* Add "true margin" to products XLSX export. + +* Add initial ``VersionMasterView`` base class. + +* Add views for ``PendingProduct`` model; also ``DepartmentWidget``. + + 0.8.170 (2021-11-09) -------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index fab44e5f..11009583 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.8.170' +__version__ = '0.8.171' From f1fd003dca5b6125076cb1564d85e0fbe99d0925 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 11 Nov 2021 12:30:00 -0600 Subject: [PATCH 0005/1189] Add permission for viewing "all" employees previously we showed all if user had "edit" perm --- tailbone/views/employees.py | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tailbone/views/employees.py b/tailbone/views/employees.py index 3ad331ab..febe521e 100644 --- a/tailbone/views/employees.py +++ b/tailbone/views/employees.py @@ -83,6 +83,7 @@ class EmployeeView(MasterView): def configure_grid(self, g): super(EmployeeView, self).configure_grid(g) route_prefix = self.get_route_prefix() + use_buefy = self.get_use_buefy() # phone g.set_joiner('phone', lambda q: q.outerjoin(model.EmployeePhoneNumber, sa.and_( @@ -114,21 +115,23 @@ class EmployeeView(MasterView): g.hide_column('username') # id - if self.request.has_perm('{}.edit'.format(route_prefix)): + if self.has_perm('edit'): g.set_link('id') else: - g.hide_column('id') + g.remove('id') del g.filters['id'] # status - if self.request.has_perm('{}.edit'.format(route_prefix)): + if self.has_perm('view_all'): g.set_enum('status', self.enum.EMPLOYEE_STATUS) g.filters['status'].default_active = True g.filters['status'].default_verb = 'equal' - # TODO: why must we set unicode string value here? - g.filters['status'].default_value = six.text_type(self.enum.EMPLOYEE_STATUS_CURRENT) + if use_buefy: + g.filters['status'].default_value = six.text_type(self.enum.EMPLOYEE_STATUS_CURRENT) + else: + g.filters['status'].default_value = self.enum.EMPLOYEE_STATUS_CURRENT else: - g.hide_column('status') + g.remove('status') del g.filters['status'] g.filters['first_name'].default_active = True @@ -151,7 +154,7 @@ class EmployeeView(MasterView): def query(self, session): q = session.query(model.Employee).join(model.Person) - if not self.request.has_perm('employees.edit'): + if not self.has_perm('view_all'): q = q.filter(model.Employee.status == self.enum.EMPLOYEE_STATUS_CURRENT) return q @@ -310,6 +313,21 @@ class EmployeeView(MasterView): (model.EmployeeDepartment, 'employee_uuid'), ] + @classmethod + def defaults(cls, config): + cls._defaults(config) + cls._employee_defaults(config) + + @classmethod + def _employee_defaults(cls, config): + permission_prefix = cls.get_permission_prefix() + model_title_plural = cls.get_model_title_plural() + + # view *all* employees + config.add_tailbone_permission(permission_prefix, + '{}.view_all'.format(permission_prefix), + "View *all* (not just current) {}".format(model_title_plural)) + def includeme(config): EmployeeView.defaults(config) From 6e15d59a843d0a1fa103bce35c4cde21d8718eb6 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 11 Nov 2021 12:31:42 -0600 Subject: [PATCH 0006/1189] Update changelog --- CHANGES.rst | 6 ++++++ tailbone/_version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index a579ad70..0499965e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGELOG ========= +0.8.172 (2021-11-11) +-------------------- + +* Add permission for viewing "all" employees. + + 0.8.171 (2021-11-11) -------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index 11009583..c36160ae 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.8.171' +__version__ = '0.8.172' From 3a10a4bcb7cfcfd675dd9ea680a5f42a4ccdf0fc Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 11 Nov 2021 13:37:10 -0600 Subject: [PATCH 0007/1189] Improve error handling when executing a custorder batch --- tailbone/templates/custorders/create.mako | 23 ++++++++++------------- tailbone/views/custorders/orders.py | 10 +++++++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/tailbone/templates/custorders/create.mako b/tailbone/templates/custorders/create.mako index a78eed6c..cfbc4894 100644 --- a/tailbone/templates/custorders/create.mako +++ b/tailbone/templates/custorders/create.mako @@ -483,7 +483,8 @@ @click="showAddItemDialog()"> Add Item - Add Past Item @@ -1239,6 +1240,9 @@ type: 'is-danger', duration: 2000, // 2 seconds }) + if (failure) { + failure(response) + } }) }, @@ -1250,20 +1254,13 @@ } this.submitBatchData(params, response => { - if (response.data.error) { - this.$buefy.toast.open({ - message: "Submit failed: " + response.data.error, - type: 'is-danger', - duration: 2000, // 2 seconds - }) - this.submittingOrder = false + if (response.data.next_url) { + location.href = response.data.next_url } else { - if (response.data.next_url) { - location.href = response.data.next_url - } else { - location.reload() - } + location.reload() } + }, response => { + this.submittingOrder = false }) }, diff --git a/tailbone/views/custorders/orders.py b/tailbone/views/custorders/orders.py index 79753c50..49280d96 100644 --- a/tailbone/views/custorders/orders.py +++ b/tailbone/views/custorders/orders.py @@ -779,9 +779,13 @@ class CustomerOrderView(MasterView): 'batch': self.normalize_batch(batch)} def submit_new_order(self, batch, data): - result = self.execute_new_order_batch(batch, data) - if not result: - return {'error': "Batch failed to execute"} + try: + result = self.execute_new_order_batch(batch, data) + except Exception as error: + return {'error': six.text_type(error)} + else: + if not result: + return {'error': "Batch failed to execute"} next_url = None if isinstance(result, model.CustomerOrder): From 426ba0ea344aa0bb352648e0f9117eed0a02f117 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 11 Nov 2021 17:42:59 -0600 Subject: [PATCH 0008/1189] Fix "download results" support for Products it is not enabled by default, but still should work when it is --- tailbone/templates/products/index.mako | 1 + tailbone/views/products.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/tailbone/templates/products/index.mako b/tailbone/templates/products/index.mako index 06b1e7e0..3f65cd68 100644 --- a/tailbone/templates/products/index.mako +++ b/tailbone/templates/products/index.mako @@ -76,6 +76,7 @@ <%def name="grid_tools()"> + ${parent.grid_tools()} % if label_profiles and request.has_perm('products.print_labels'): diff --git a/tailbone/views/products.py b/tailbone/views/products.py index 351dd832..2f7bf02d 100644 --- a/tailbone/views/products.py +++ b/tailbone/views/products.py @@ -732,6 +732,16 @@ class ProductView(MasterView): return row + def download_results_normalize(self, product, fields, **kwargs): + data = super(ProductView, self).download_results_normalize( + product, fields, **kwargs) + + if 'upc' in data: + if isinstance(data['upc'], GPC): + data['upc'] = six.text_type(data['upc']) + + return data + def get_instance(self): key = self.request.matchdict['uuid'] product = Session.query(model.Product).get(key) From 901dacf038c81cff0bdc0a7214b119d7e69fa1e8 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 11 Nov 2021 18:38:44 -0600 Subject: [PATCH 0009/1189] Update changelog --- CHANGES.rst | 8 ++++++++ tailbone/_version.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 0499965e..32eea392 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ CHANGELOG ========= +0.8.173 (2021-11-11) +-------------------- + +* Improve error handling when executing a custorder batch. + +* Fix "download results" support for Products. + + 0.8.172 (2021-11-11) -------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index c36160ae..e081e8ca 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.8.172' +__version__ = '0.8.173' From a7b91b5b31acd691ae7871e7af792a8dd499beda Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 13 Nov 2021 15:05:45 -0600 Subject: [PATCH 0010/1189] Expose the "sync users" flag for Roles --- tailbone/views/roles.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tailbone/views/roles.py b/tailbone/views/roles.py index 2ce48f0d..78389d5d 100644 --- a/tailbone/views/roles.py +++ b/tailbone/views/roles.py @@ -53,10 +53,15 @@ class RoleView(PrincipalMasterView): has_versions = True touchable = True + labels = { + 'sync_me': "Sync Attrs & Perms", + } + grid_columns = [ 'name', 'session_timeout', 'sync_me', + 'sync_users', 'node_type', 'notes', ] @@ -66,6 +71,7 @@ class RoleView(PrincipalMasterView): 'session_timeout', 'notes', 'sync_me', + 'sync_users', 'node_type', 'users', 'permissions', @@ -178,10 +184,11 @@ class RoleView(PrincipalMasterView): elif role is guest_role(self.Session()): include = False if not include: - f.remove('sync_me', 'node_type') + f.remove('sync_me', 'sync_users', 'node_type') else: if not self.has_perm('edit_node_sync'): f.set_readonly('sync_me') + f.set_readonly('sync_users') f.set_readonly('node_type') # notes From f385aab44a64865986055309fd65833ac8496af8 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 14 Nov 2021 13:27:13 -0600 Subject: [PATCH 0011/1189] Update changelog --- CHANGES.rst | 6 ++++++ tailbone/_version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 32eea392..dab12a4f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGELOG ========= +0.8.174 (2021-11-14) +-------------------- + +* Expose the "sync users" flag for Roles. + + 0.8.173 (2021-11-11) -------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index e081e8ca..586e661e 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.8.173' +__version__ = '0.8.174' From 0fa888efafba732f9de7450f795d3f072227d944 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 16 Nov 2021 17:23:56 -0600 Subject: [PATCH 0012/1189] Fix bug when product has empty suggested price --- tailbone/views/products.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tailbone/views/products.py b/tailbone/views/products.py index 2f7bf02d..a59df32f 100644 --- a/tailbone/views/products.py +++ b/tailbone/views/products.py @@ -558,8 +558,10 @@ class ProductView(MasterView): def render_suggested_price(self, product, column): text = self.render_price(product, column) + if not text: + return - if text and self.show_price_effective_dates(): + if self.show_price_effective_dates(): history = self.get_suggested_price_history(product) if history: date = localtime(self.rattail_config, history[0]['changed'], from_utc=True).date() From b8f1b7bd84fc57ccd30269391503834bff88f1c2 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 17 Nov 2021 14:57:10 -0600 Subject: [PATCH 0013/1189] Show ordered quantity when viewing costing batch row --- tailbone/views/purchasing/costing.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tailbone/views/purchasing/costing.py b/tailbone/views/purchasing/costing.py index 71b99cf2..d790fbc1 100644 --- a/tailbone/views/purchasing/costing.py +++ b/tailbone/views/purchasing/costing.py @@ -129,6 +129,8 @@ class CostingBatchView(PurchasingBatchView): 'size', 'department_name', 'case_quantity', + 'cases_ordered', + 'units_ordered', 'cases_shipped', 'units_shipped', 'cases_received', From e8828efae3b6a3bdf51fe13050535ca2e36ce1fb Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 17 Nov 2021 15:12:54 -0600 Subject: [PATCH 0014/1189] Update changelog --- CHANGES.rst | 8 ++++++++ tailbone/_version.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index dab12a4f..607d6443 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ CHANGELOG ========= +0.8.175 (2021-11-17) +-------------------- + +* Fix bug when product has empty suggested price. + +* Show ordered quantity when viewing costing batch row. + + 0.8.174 (2021-11-14) -------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index 586e661e..79b5759b 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.8.174' +__version__ = '0.8.175' From 03dad826636c520168acfbbfcf034e50e4262aba Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 25 Nov 2021 16:50:13 -0600 Subject: [PATCH 0015/1189] Add basic support for receiving from PO with invoice --- tailbone/views/purchasing/receiving.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tailbone/views/purchasing/receiving.py b/tailbone/views/purchasing/receiving.py index ead0abed..ccc97d9a 100644 --- a/tailbone/views/purchasing/receiving.py +++ b/tailbone/views/purchasing/receiving.py @@ -457,7 +457,7 @@ class ReceivingBatchView(PurchasingBatchView): f.set_widget('store_uuid', dfwidget.HiddenWidget()) # purchase - if (self.creating and workflow == 'from_po' + if (self.creating and workflow in ('from_po', 'from_po_with_invoice') and self.purchase_order_fieldname == 'purchase'): if use_buefy: f.replace('purchase', 'purchase_uuid') @@ -507,6 +507,11 @@ class ReceivingBatchView(PurchasingBatchView): 'invoice_file', 'invoice_parser_key') + elif workflow == 'from_po_with_invoice': + f.remove('truck_dump_batch_uuid') + f.set_required('invoice_file') + f.set_required('invoice_parser_key') + elif workflow == 'truck_dump_children_first': f.remove('truck_dump_batch_uuid', 'invoice_file', @@ -561,6 +566,9 @@ class ReceivingBatchView(PurchasingBatchView): elif batch_type == 'from_po': # TODO: how to best handle this field? this doesn't seem flexible kwargs['purchase_key'] = batch.purchase_uuid + elif batch_type == 'from_po_with_invoice': + # TODO: how to best handle this field? this doesn't seem flexible + kwargs['purchase_key'] = batch.purchase_uuid elif batch_type == 'truck_dump_children_first': kwargs['truck_dump'] = True kwargs['truck_dump_children_first'] = True From b9037111a4a8370f2b7f7b472e53247b7c37899d Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 25 Nov 2021 18:56:28 -0600 Subject: [PATCH 0016/1189] Don't use multi-select for new report in buefy themes also let app handler fetch the report handler --- tailbone/views/reports.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tailbone/views/reports.py b/tailbone/views/reports.py index 6f6b1660..5d1ca5eb 100644 --- a/tailbone/views/reports.py +++ b/tailbone/views/reports.py @@ -2,7 +2,7 @@ ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2019 Lance Edgar +# Copyright © 2010-2021 Lance Edgar # # This file is part of Rattail. # @@ -36,7 +36,6 @@ import rattail from rattail.db import model, Session as RattailSession from rattail.files import resource_path from rattail.time import localtime -from rattail.reporting import get_report_handler from rattail.threads import Thread from rattail.util import simple_error, OrderedDict @@ -286,7 +285,8 @@ class GenerateReport(View): self.handler = self.get_handler() def get_handler(self): - return get_report_handler(self.rattail_config) + app = self.get_rattail_app() + return app.get_report_handler() def choose(self): """ @@ -313,7 +313,7 @@ class GenerateReport(View): values = [(r.type_key, r.name) for r in reports.values()] values.sort(key=lambda r: r[1]) if use_buefy: - form.set_widget('report_type', forms.widgets.CustomSelectWidget(values=values, size=10)) + form.set_widget('report_type', forms.widgets.CustomSelectWidget(values=values)) form.widgets['report_type'].set_template_values(input_handler='reportTypeChanged') else: form.set_widget('report_type', forms.widgets.PlainSelectWidget(values=values, size=10)) From ce354d5bc32b07a1316942a024491a8ca3d9bf77 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 25 Nov 2021 19:01:35 -0600 Subject: [PATCH 0017/1189] Update changelog --- CHANGES.rst | 8 ++++++++ tailbone/_version.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 607d6443..7287c0cd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ CHANGELOG ========= +0.8.176 (2021-11-25) +-------------------- + +* Add basic support for receiving from PO with invoice. + +* Don't use multi-select for new report in buefy themes. + + 0.8.175 (2021-11-17) -------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index 79b5759b..939e3843 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.8.175' +__version__ = '0.8.176' From c1f91906134c91f9c1ae439220be369f94e017f2 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 27 Nov 2021 19:08:15 -0600 Subject: [PATCH 0018/1189] Show current/sale pricing for products in new custorder page --- tailbone/templates/custorders/create.mako | 69 ++++++++++++++++++++--- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/tailbone/templates/custorders/create.mako b/tailbone/templates/custorders/create.mako index cfbc4894..98dafda4 100644 --- a/tailbone/templates/custorders/create.mako +++ b/tailbone/templates/custorders/create.mako @@ -552,6 +552,10 @@ {{ productSize }} + + {{ productCaseQuantity }} + + 2021-01-01 --> - - {{ productCaseQuantity }} + + + {{ productSalePriceDisplay }} + + + + + + {{ productSaleEndsDisplay }} + @@ -622,6 +636,20 @@ + + + {{ productSalePriceDisplay }} + + + + + + {{ productSaleEndsDisplay }} + + + {{ productCaseQuantity }} @@ -629,7 +657,9 @@ {{ productCasePriceDisplay }} @@ -702,11 +732,6 @@ field="description" sortable> {{ props.row.description }} - - - {{ props.row.size }} @@ -716,6 +741,22 @@ {{ props.row.unit_price_display }} + + + {{ props.row.sale_price_display }} + + + + + + {{ props.row.sale_ends_display }} + + + @@ -933,6 +974,8 @@ productCaseQuantity: null, productUnitPriceDisplay: null, productCasePriceDisplay: null, + productSalePriceDisplay: null, + productSaleEndsDisplay: null, productURL: null, productImageURL: null, productQuantity: null, @@ -1486,6 +1529,8 @@ this.productCaseQuantity = null this.productUnitPriceDisplay = null this.productCasePriceDisplay = null + this.productSalePriceDisplay = null + this.productSaleEndsDisplay = null this.productImageURL = '${request.static_url('tailbone:static/img/product.png')}' this.productQuantity = 1 this.productUnitChoices = this.defaultUnitChoices @@ -1533,6 +1578,8 @@ this.productCaseQuantity = selected.case_quantity this.productUnitPriceDisplay = selected.unit_price_display this.productCasePriceDisplay = selected.case_price_display + this.productSalePriceDisplay = selected.sale_price_display + this.productSaleEndsDisplay = selected.sale_ends_display this.productImageURL = selected.image_url this.productURL = selected.url this.productQuantity = 1 @@ -1561,6 +1608,8 @@ this.productURL = row.product_url this.productUnitPriceDisplay = row.unit_price_display this.productCasePriceDisplay = row.case_price_display + this.productSalePriceDisplay = row.sale_price_display + this.productSaleEndsDisplay = row.sale_ends_display this.productImageURL = row.product_image_url this.productQuantity = row.order_quantity this.productUnitChoices = row.order_uom_choices @@ -1606,6 +1655,8 @@ this.productCaseQuantity = null this.productUnitPriceDisplay = null this.productCasePriceDisplay = null + this.productSalePriceDisplay = null + this.productSaleEndsDisplay = null this.productURL = null this.productImageURL = null this.productUnitChoices = this.defaultUnitChoices @@ -1651,6 +1702,8 @@ this.productCaseQuantity = response.data.case_quantity this.productUnitPriceDisplay = response.data.unit_price_display this.productCasePriceDisplay = response.data.case_price_display + this.productSalePriceDisplay = response.data.sale_price_display + this.productSaleEndsDisplay = response.data.sale_ends_display this.productURL = response.data.url this.productImageURL = response.data.image_url this.setProductUnitChoices(response.data.uom_choices) From dbd00291b33a393a3cc3aa711264240b93174fcd Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 27 Nov 2021 19:47:02 -0600 Subject: [PATCH 0019/1189] Add simple search filters for past items dialog in new custorder --- tailbone/templates/custorders/create.mako | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tailbone/templates/custorders/create.mako b/tailbone/templates/custorders/create.mako index 98dafda4..0071b61f 100644 --- a/tailbone/templates/custorders/create.mako +++ b/tailbone/templates/custorders/create.mako @@ -713,7 +713,8 @@ :selected.sync="pastItemsSelected" sortable paginated - per-page="6"> + per-page="5" + :debounce-search="1000">