From 70ee7848187ca5b2dc63eb286a744719222852ee Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 20 Jun 2023 17:06:20 -0500 Subject: [PATCH 001/542] Include user "active" flag in profile view context whoops, missed that one.. --- tailbone/views/people.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tailbone/views/people.py b/tailbone/views/people.py index 29b93b9a..8dc96037 100644 --- a/tailbone/views/people.py +++ b/tailbone/views/people.py @@ -811,6 +811,7 @@ class PersonView(MasterView): 'username': user.username, 'display_name': user.display_name, 'email_address': app.get_contact_email_address(user), + 'active': user.active, 'view_url': self.request.route_url('users.view', uuid=user.uuid), } From 8cc6def93ea2b990a7df0361ac73da71b998231c Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 20 Jun 2023 17:06:54 -0500 Subject: [PATCH 002/542] 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 1f64e4ea..6225b749 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGELOG ========= +0.9.36 (2023-06-20) +------------------- + +* Include user "active" flag in profile view context. + + 0.9.35 (2023-06-20) ------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index 89a33ad0..d43dbe86 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.9.35' +__version__ = '0.9.36' From 08a75f6e9f38d3103d9e1ffbf01989953f93bab0 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 27 Jun 2023 12:37:00 -0500 Subject: [PATCH 003/542] Avoid deprecated product key field getter --- tailbone/views/products.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tailbone/views/products.py b/tailbone/views/products.py index 8988538b..1cfa528a 100644 --- a/tailbone/views/products.py +++ b/tailbone/views/products.py @@ -238,8 +238,7 @@ class ProductView(MasterView): ProductCostCodeAny.product_uuid == model.Product.uuid) # product key - key = self.rattail_config.product_key() - field = self.product_key_fields.get(key, key) + field = self.get_product_key_field() g.filters[field].default_active = True g.filters[field].default_verb = 'equal' g.set_sort_defaults(field) @@ -1253,8 +1252,7 @@ class ProductView(MasterView): } def get_panel_fields_main(self, product): - key = self.rattail_config.product_key() - product_key_field = self.product_key_fields.get(key, key) + product_key_field = self.get_product_key_field() fields = [ product_key_field, 'brand', From 1be26b7f33bc11d1446771044bd3a1f07005bbdf Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 27 Jun 2023 12:37:16 -0500 Subject: [PATCH 004/542] Allow "arbitrary" PO attachment to purchase batch for sake of other POS integration etc. --- tailbone/views/purchasing/batch.py | 142 +++++++++---------------- tailbone/views/purchasing/costing.py | 6 +- tailbone/views/purchasing/ordering.py | 1 - tailbone/views/purchasing/receiving.py | 36 ++++--- 4 files changed, 74 insertions(+), 111 deletions(-) diff --git a/tailbone/views/purchasing/batch.py b/tailbone/views/purchasing/batch.py index 16153f64..8960a522 100644 --- a/tailbone/views/purchasing/batch.py +++ b/tailbone/views/purchasing/batch.py @@ -24,7 +24,7 @@ Base class for purchasing batch views """ -from rattail.db import model, api +from rattail.db.model import PurchaseBatch, PurchaseBatchRow import colander from deform import widget as dfwidget @@ -40,8 +40,8 @@ class PurchasingBatchView(BatchMasterView): Master view base class, for purchase batches. The views for both "ordering" and "receiving" batches will inherit from this. """ - model_class = model.PurchaseBatch - model_row_class = model.PurchaseBatchRow + model_class = PurchaseBatch + model_row_class = PurchaseBatchRow default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler' supports_new_product = False cloneable = True @@ -160,11 +160,13 @@ class PurchasingBatchView(BatchMasterView): raise NotImplementedError("Please define `batch_mode` for your purchasing batch view") def query(self, session): + model = self.model return session.query(model.PurchaseBatch)\ .filter(model.PurchaseBatch.mode == self.batch_mode) def configure_grid(self, g): - super(PurchasingBatchView, self).configure_grid(g) + super().configure_grid(g) + model = self.model g.joiners['vendor'] = lambda q: q.join(model.Vendor) g.filters['vendor'] = g.make_filter('vendor', model.Vendor.name, @@ -309,7 +311,7 @@ class PurchasingBatchView(BatchMasterView): if buyer: buyer_display = str(buyer) elif self.creating: - buyer = self.request.user.employee + buyer = app.get_employee(self.request.user) if buyer: buyer_display = str(buyer) f.set_default('buyer_uuid', buyer.uuid) @@ -405,12 +407,30 @@ class PurchasingBatchView(BatchMasterView): return tags.link_to(text, url) def render_purchase(self, batch, field): - purchase = batch.purchase + model = self.model + + # default logic can only render the "normal" (built-in) + # purchase field; anything else must be handled by view + # supplement if possible + if field != 'purchase': + for supp in self.iter_view_supplements(): + renderer = getattr(supp, f'render_purchase_{field}', None) + if renderer: + return renderer(batch) + + # nothing to render if no purchase found + purchase = getattr(batch, field) if not purchase: - return "" + return + + # render link to native purchase, if possible text = str(purchase) - url = self.request.route_url('purchases.view', uuid=purchase.uuid) - return tags.link_to(text, url) + if isinstance(purchase, model.Purchase): + url = self.request.route_url('purchases.view', uuid=purchase.uuid) + return tags.link_to(text, url) + + # otherwise just render purchase as-is + return text def render_vendor_email(self, batch, field): if batch.vendor.email: @@ -448,12 +468,14 @@ class PurchasingBatchView(BatchMasterView): return text def get_store_values(self): + model = self.model stores = self.Session.query(model.Store)\ .order_by(model.Store.id) return [(s.uuid, "({}) {}".format(s.id, s.name)) for s in stores] def get_vendors(self): + model = self.model return self.Session.query(model.Vendor)\ .order_by(model.Vendor.name) @@ -463,6 +485,7 @@ class PurchasingBatchView(BatchMasterView): for v in vendors] def get_buyers(self): + model = self.model return self.Session.query(model.Employee)\ .join(model.Person)\ .filter(model.Employee.status == self.enum.EMPLOYEE_STATUS_CURRENT)\ @@ -474,6 +497,7 @@ class PurchasingBatchView(BatchMasterView): for b in buyers] def get_department_options(self): + model = self.model departments = self.Session.query(model.Department).order_by(model.Department.number) return [('{} {}'.format(d.number, d.name), d.uuid) for d in departments] @@ -487,39 +511,10 @@ class PurchasingBatchView(BatchMasterView): if phone.type == 'Fax': return phone.number - def eligible_purchases(self, vendor_uuid=None, mode=None): - if not vendor_uuid: - vendor_uuid = self.request.GET.get('vendor_uuid') - vendor = self.Session.get(model.Vendor, vendor_uuid) if vendor_uuid else None - if not vendor: - return {'error': "Must specify a vendor."} - - if mode is None: - mode = self.request.GET.get('mode') - mode = int(mode) if mode and mode.isdigit() else None - if not mode or mode not in self.enum.PURCHASE_BATCH_MODE: - return {'error': "Unknown mode: {}".format(mode)} - - purchases = self.handler.get_eligible_purchases(vendor, mode) - return self.get_eligible_purchases_data(purchases) - - def get_eligible_purchases_data(self, purchases): - return {'purchases': [{'key': p.uuid, - 'department_uuid': p.department_uuid or '', - 'display': self.render_eligible_purchase(p)} - for p in purchases]} - - def render_eligible_purchase(self, purchase): - if purchase.status == self.enum.PURCHASE_STATUS_ORDERED: - date = purchase.date_ordered - total = purchase.po_total - elif purchase.status == self.enum.PURCHASE_STATUS_RECEIVED: - date = purchase.date_received - total = purchase.invoice_total - return '{} for ${:0,.2f} ({})'.format(date, total or 0, purchase.department or purchase.buyer) - def get_batch_kwargs(self, batch, **kwargs): - kwargs = super(PurchasingBatchView, self).get_batch_kwargs(batch, **kwargs) + kwargs = super().get_batch_kwargs(batch, **kwargs) + model = self.model + kwargs['mode'] = self.batch_mode kwargs['truck_dump'] = batch.truck_dump kwargs['invoice_parser_key'] = batch.invoice_parser_key @@ -565,16 +560,20 @@ class PurchasingBatchView(BatchMasterView): if self.batch_mode in (self.enum.PURCHASE_BATCH_MODE_RECEIVING, self.enum.PURCHASE_BATCH_MODE_COSTING): - purchase = batch.purchase - if not purchase and batch.purchase_uuid: - purchase = self.Session.get(model.Purchase, batch.purchase_uuid) - assert purchase - if purchase: - kwargs['purchase'] = purchase - kwargs['buyer'] = purchase.buyer - kwargs['buyer_uuid'] = purchase.buyer_uuid - kwargs['date_ordered'] = purchase.date_ordered - kwargs['po_total'] = purchase.po_total + field = self.batch_handler.get_purchase_order_fieldname() + if field == 'purchase': + purchase = batch.purchase + if not purchase and batch.purchase_uuid: + purchase = self.Session.get(model.Purchase, batch.purchase_uuid) + assert purchase + if purchase: + kwargs['purchase'] = purchase + kwargs['buyer'] = purchase.buyer + kwargs['buyer_uuid'] = purchase.buyer_uuid + kwargs['date_ordered'] = purchase.date_ordered + kwargs['po_total'] = purchase.po_total + elif hasattr(batch, field): + kwargs[field] = getattr(batch, field) return kwargs @@ -826,25 +825,6 @@ class PurchasingBatchView(BatchMasterView): return HTML.literal( g.render_buefy_table_element(data_prop='rowData.credits')) -# def item_lookup(self, value, field=None): -# """ -# Try to locate a single product using ``value`` as a lookup code. -# """ -# batch = self.get_instance() -# product = api.get_product_by_vendor_code(Session(), value, vendor=batch.vendor) -# if product: -# return product.uuid -# if value.isdigit(): -# product = api.get_product_by_upc(Session(), GPC(value)) -# if not product: -# product = api.get_product_by_upc(Session(), GPC(value, calc_check_digit='upc')) -# if product: -# if not product.cost_for_vendor(batch.vendor): -# raise fa.ValidationError("Product {} exists but has no cost for vendor {}".format( -# product.upc.pretty(), batch.vendor)) -# return product.uuid -# raise fa.ValidationError("Product not found") - # def before_create_row(self, form): # row = form.fieldset.model # batch = self.get_instance() @@ -937,28 +917,6 @@ class PurchasingBatchView(BatchMasterView): # return self.get_action_url('view', batch) - @classmethod - def _purchasing_defaults(cls, config): - rattail_config = config.registry.settings.get('rattail_config') - route_prefix = cls.get_route_prefix() - url_prefix = cls.get_url_prefix() - permission_prefix = cls.get_permission_prefix() - model_key = cls.get_model_key() - model_title = cls.get_model_title() - - # eligible purchases (AJAX) - config.add_route('{}.eligible_purchases'.format(route_prefix), '{}/eligible-purchases'.format(url_prefix)) - config.add_view(cls, attr='eligible_purchases', route_name='{}.eligible_purchases'.format(route_prefix), - renderer='json', permission='{}.view'.format(permission_prefix)) - - - @classmethod - def defaults(cls, config): - cls._purchasing_defaults(config) - cls._batch_defaults(config) - cls._defaults(config) - - class NewProduct(colander.Schema): item_id = colander.SchemaNode(colander.String()) diff --git a/tailbone/views/purchasing/costing.py b/tailbone/views/purchasing/costing.py index 294b29ef..ec4e3ee3 100644 --- a/tailbone/views/purchasing/costing.py +++ b/tailbone/views/purchasing/costing.py @@ -43,8 +43,6 @@ class CostingBatchView(PurchasingBatchView): downloadable = True bulk_deletable = True - purchase_order_fieldname = 'purchase' - labels = { 'invoice_parser_key': "Invoice Parser", } @@ -290,8 +288,9 @@ class CostingBatchView(PurchasingBatchView): f.remove_field('batch_type') # purchase + field = self.batch_handler.get_purchase_order_fieldname() if (self.creating and workflow == 'invoice_with_po' - and self.purchase_order_fieldname == 'purchase'): + and field == 'purchase'): f.replace('purchase', 'purchase_uuid') purchases = self.handler.get_eligible_purchases( vendor, self.enum.PURCHASE_BATCH_MODE_COSTING) @@ -317,7 +316,6 @@ class CostingBatchView(PurchasingBatchView): @classmethod def defaults(cls, config): cls._costing_defaults(config) - cls._purchasing_defaults(config) cls._batch_defaults(config) cls._defaults(config) diff --git a/tailbone/views/purchasing/ordering.py b/tailbone/views/purchasing/ordering.py index b0b00402..03308d07 100644 --- a/tailbone/views/purchasing/ordering.py +++ b/tailbone/views/purchasing/ordering.py @@ -486,7 +486,6 @@ class OrderingBatchView(PurchasingBatchView): @classmethod def defaults(cls, config): cls._ordering_defaults(config) - cls._purchasing_defaults(config) cls._batch_defaults(config) cls._defaults(config) diff --git a/tailbone/views/purchasing/receiving.py b/tailbone/views/purchasing/receiving.py index cdc69fe5..e659123a 100644 --- a/tailbone/views/purchasing/receiving.py +++ b/tailbone/views/purchasing/receiving.py @@ -87,8 +87,6 @@ class ReceivingBatchView(PurchasingBatchView): default_uom_is_case = True - purchase_order_fieldname = 'purchase' - labels = { 'truck_dump_batch': "Truck Dump Parent", 'invoice_parser_key': "Invoice Parser", @@ -390,7 +388,7 @@ class ReceivingBatchView(PurchasingBatchView): return title def configure_form(self, f): - super(ReceivingBatchView, self).configure_form(f) + super().configure_form(f) model = self.model batch = f.model_instance allow_truck_dump = self.batch_handler.allow_truck_dump_receiving() @@ -498,18 +496,28 @@ class ReceivingBatchView(PurchasingBatchView): f.set_widget('store_uuid', dfwidget.HiddenWidget()) # purchase - if (self.creating and workflow in ('from_po', 'from_po_with_invoice') - and self.purchase_order_fieldname == 'purchase'): - f.replace('purchase', 'purchase_uuid') + field = self.batch_handler.get_purchase_order_fieldname() + if field == 'purchase': + field = 'purchase_uuid' + # TODO: workflow "invoice_with_po" is for costing mode, should rename? + if self.creating and workflow in ( + 'from_po', 'from_po_with_invoice', 'invoice_with_po'): + f.replace('purchase', field) purchases = self.batch_handler.get_eligible_purchases( - vendor, self.enum.PURCHASE_BATCH_MODE_RECEIVING) - values = [(p.uuid, self.batch_handler.render_eligible_purchase(p)) + vendor, self.batch_mode) + values = [(self.batch_handler.get_eligible_purchase_key(p), + self.batch_handler.render_eligible_purchase(p)) for p in purchases] - f.set_widget('purchase_uuid', dfwidget.SelectWidget(values=values)) - f.set_label('purchase_uuid', "Purchase Order") - f.set_required('purchase_uuid') - elif self.creating or not batch.purchase: + f.set_widget(field, dfwidget.SelectWidget(values=values)) + if field == 'purchase_uuid': + f.set_label(field, "Purchase Order") + f.set_required(field) + elif self.creating: f.remove_field('purchase') + else: # not creating + if field != 'purchase_uuid': + f.replace('purchase', field) + f.set_renderer(field, self.render_purchase) # department if self.creating: @@ -939,8 +947,9 @@ class ReceivingBatchView(PurchasingBatchView): Assign the original purchase order to the given batch. Default behavior assumes a Rattail Purchase object is what we're after. """ + field = self.batch_handler.get_purchase_order_fieldname() purchase = self.handler.assign_purchase_order( - batch, po_form.validated[self.purchase_order_fieldname], + batch, po_form.validated[field], session=self.Session()) department = self.department_for_purchase(purchase) @@ -1992,7 +2001,6 @@ class ReceivingBatchView(PurchasingBatchView): @classmethod def defaults(cls, config): cls._receiving_defaults(config) - cls._purchasing_defaults(config) cls._batch_defaults(config) cls._defaults(config) From 8742a03e18c80463b489b3f1f8f8bd22a0333493 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 3 Jul 2023 09:52:42 -0500 Subject: [PATCH 005/542] 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 6225b749..d71eac97 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ CHANGELOG ========= +0.9.37 (2023-07-03) +------------------- + +* Avoid deprecated product key field getter. + +* Allow "arbitrary" PO attachment to purchase batch. + + 0.9.36 (2023-06-20) ------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index d43dbe86..d57f5f68 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.9.36' +__version__ = '0.9.37' From 58f9b3ce2a5f560ca567d0860c2417751c77f8cf Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 6 Jul 2023 21:23:44 -0500 Subject: [PATCH 006/542] Optimize "auto-receive" batch process disable versioning when doing "auto-receive" for a receiving batch --- tailbone/views/batch/core.py | 8 ++- tailbone/views/purchasing/receiving.py | 85 +++++++------------------- 2 files changed, 29 insertions(+), 64 deletions(-) diff --git a/tailbone/views/batch/core.py b/tailbone/views/batch/core.py index e2eeeda4..1f5e2be9 100644 --- a/tailbone/views/batch/core.py +++ b/tailbone/views/batch/core.py @@ -931,7 +931,7 @@ class BatchMasterView(MasterView): prefix = self.rattail_config.get('rattail', 'command_prefix', default=sys.prefix) cmd = [os.path.join(prefix, 'bin/{}'.format(command))] - for path in self.rattail_config.files_read: + for path in reversed(self.rattail_config.files_read): cmd.extend(['--config', path]) if username: cmd.extend(['--runas', username]) @@ -969,6 +969,10 @@ class BatchMasterView(MasterView): batch_uuid = key[0] # figure out the (sub)command args we'll be passing + if handler_action == 'auto_receive': + subcommand = 'auto-receive' + else: + subcommand = f'{handler_action}-batch' subargs = [ '--batch-type', self.handler.batch_key, @@ -987,7 +991,7 @@ class BatchMasterView(MasterView): command_args=[ '--no-versioning', ], - subcommand='{}-batch'.format(handler_action), + subcommand=subcommand, subcommand_args=subargs) except Exception as error: log.warning("%s of '%s' batch failed: %s", handler_action, self.handler.batch_key, batch_uuid, exc_info=True) diff --git a/tailbone/views/purchasing/receiving.py b/tailbone/views/purchasing/receiving.py index e659123a..1d1479d6 100644 --- a/tailbone/views/purchasing/receiving.py +++ b/tailbone/views/purchasing/receiving.py @@ -34,10 +34,8 @@ import humanize import sqlalchemy as sa from rattail import pod -from rattail.db import model, Session as RattailSession from rattail.time import localtime, make_utc from rattail.util import pretty_quantity, prettify, simple_error -from rattail.threads import Thread import colander from deform import widget as dfwidget @@ -252,6 +250,7 @@ class ReceivingBatchView(PurchasingBatchView): :meth:`tailbone.views.purchasing.costing:CostingBatchView.create()` which uses similar logic. """ + model = self.model route_prefix = self.get_route_prefix() workflows = self.handler.supported_receiving_workflows() valid_workflows = [workflow['workflow_key'] @@ -642,7 +641,8 @@ class ReceivingBatchView(PurchasingBatchView): return params def template_kwargs_create(self, **kwargs): - kwargs = super(ReceivingBatchView, self).template_kwargs_create(**kwargs) + kwargs = super().template_kwargs_create(**kwargs) + model = self.model if self.handler.allow_truck_dump_receiving(): vmap = {} batches = self.Session.query(model.PurchaseBatch)\ @@ -931,16 +931,17 @@ class ReceivingBatchView(PurchasingBatchView): url = self.request.route_url('receiving.view', uuid=truck_dump.uuid) return tags.link_to(text, url) - @staticmethod - @colander.deferred - def validate_purchase(node, kw): - session = kw['session'] - def validate(node, value): - purchase = session.get(model.Purchase, value) - if not purchase: - raise colander.Invalid(node, "Purchase not found") - return purchase.uuid - return validate + # TODO: is this actually used? wait to see if something breaks.. + # @staticmethod + # @colander.deferred + # def validate_purchase(node, kw): + # session = kw['session'] + # def validate(node, value): + # purchase = session.get(model.Purchase, value) + # if not purchase: + # raise colander.Invalid(node, "Purchase not found") + # return purchase.uuid + # return validate def assign_purchase_order(self, batch, po_form): """ @@ -957,7 +958,8 @@ class ReceivingBatchView(PurchasingBatchView): batch.department_uuid = department.uuid def configure_row_grid(self, g): - super(ReceivingBatchView, self).configure_row_grid(g) + super().configure_row_grid(g) + model = self.model batch = self.get_instance() # vendor_code @@ -1469,6 +1471,7 @@ class ReceivingBatchView(PurchasingBatchView): a "pack" item, such that it instead associates with the "unit" item, with quantities adjusted accordingly. """ + model = self.model batch = self.get_instance() row_uuid = self.request.params.get('row_uuid') @@ -1513,7 +1516,8 @@ class ReceivingBatchView(PurchasingBatchView): }) def configure_row_form(self, f): - super(ReceivingBatchView, self).configure_row_form(f) + super().configure_row_form(f) + model = self.model batch = self.get_instance() # when viewing a row which has no product reference, enable @@ -1690,6 +1694,7 @@ class ReceivingBatchView(PurchasingBatchView): return True def save_edit_row_form(self, form): + model = self.model batch = self.get_instance() row = self.objectify(form) @@ -1829,6 +1834,7 @@ class ReceivingBatchView(PurchasingBatchView): """ AJAX view for updating various cost fields in a data row. """ + model = self.model batch = self.get_instance() data = dict(get_form_data(self.request)) @@ -1882,55 +1888,10 @@ class ReceivingBatchView(PurchasingBatchView): def auto_receive(self): """ - View which can "auto-receive" all items in the batch. Meant only as a - convenience for developers. + View which can "auto-receive" all items in the batch. """ batch = self.get_instance() - key = '{}.receive_all'.format(self.get_grid_key()) - progress = self.make_progress(key) - kwargs = {'progress': progress} - thread = Thread(target=self.auto_receive_thread, args=(batch.uuid, self.request.user.uuid), kwargs=kwargs) - thread.start() - - return self.render_progress(progress, { - 'instance': batch, - 'cancel_url': self.get_action_url('view', batch), - 'cancel_msg': "Auto-receive was canceled", - }) - - def auto_receive_thread(self, uuid, user_uuid, progress=None): - """ - Thread target for receiving all items on the given batch. - """ - session = RattailSession() - batch = session.get(model.PurchaseBatch, uuid) - # user = session.query(model.User).get(user_uuid) - try: - self.handler.auto_receive_all_items(batch, progress=progress) - - # if anything goes wrong, rollback and log the error etc. - except Exception as error: - session.rollback() - log.exception("auto-receive failed for: %s".format(batch)) - session.close() - if progress: - progress.session.load() - progress.session['error'] = True - progress.session['error_msg'] = "Auto-receive failed: {}".format( - simple_error(error)) - progress.session.save() - - # if no error, check result flag (false means user canceled) - else: - session.commit() - session.refresh(batch) - success_url = self.get_action_url('view', batch) - session.close() - if progress: - progress.session.load() - progress.session['complete'] = True - progress.session['success_url'] = success_url - progress.session.save() + return self.handler_action(batch, 'auto_receive') def configure_get_simple_settings(self): config = self.rattail_config From 6b6e358dbe8e497622a1cdb2aa4b5b35997a147f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 7 Jul 2023 15:38:08 -0500 Subject: [PATCH 007/542] 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 d71eac97..727bd250 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGELOG ========= +0.9.38 (2023-07-07) +------------------- + +* Optimize "auto-receive" batch process. + + 0.9.37 (2023-07-03) ------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index d57f5f68..aa6a7d76 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.9.37' +__version__ = '0.9.38' From 4729785b0506a2aafb5e77ce79999cafaae02f74 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 7 Jul 2023 17:19:08 -0500 Subject: [PATCH 008/542] Show invoice number for each row in receiving --- tailbone/templates/receiving/view_row.mako | 1 + tailbone/views/purchasing/receiving.py | 1 + 2 files changed, 2 insertions(+) diff --git a/tailbone/templates/receiving/view_row.mako b/tailbone/templates/receiving/view_row.mako index 4d596391..2341cd3e 100644 --- a/tailbone/templates/receiving/view_row.mako +++ b/tailbone/templates/receiving/view_row.mako @@ -444,6 +444,7 @@

Invoice

+ ${form.render_field_readonly('invoice_number')} ${form.render_field_readonly('invoice_line_number')} ${form.render_field_readonly('invoice_unit_cost')} ${form.render_field_readonly('invoice_case_size')} diff --git a/tailbone/views/purchasing/receiving.py b/tailbone/views/purchasing/receiving.py index 1d1479d6..d4bed60a 100644 --- a/tailbone/views/purchasing/receiving.py +++ b/tailbone/views/purchasing/receiving.py @@ -206,6 +206,7 @@ class ReceivingBatchView(PurchasingBatchView): 'po_unit_cost', 'po_case_size', 'po_total', + 'invoice_number', 'invoice_line_number', 'invoice_unit_cost', 'invoice_cost_confirmed', From a84bcf688bb90591e4bc7820c6c7fc67eb3375d4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 7 Jul 2023 17:56:45 -0500 Subject: [PATCH 009/542] Tweak display options for tempmon probe readings graph --- tailbone/templates/tempmon/probes/graph.mako | 12 ++++++++++-- tailbone/views/tempmon/probes.py | 20 ++++---------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/tailbone/templates/tempmon/probes/graph.mako b/tailbone/templates/tempmon/probes/graph.mako index 795af145..412f25dd 100644 --- a/tailbone/templates/tempmon/probes/graph.mako +++ b/tailbone/templates/tempmon/probes/graph.mako @@ -39,7 +39,13 @@ - ${time_range} + + + + + +
@@ -86,7 +92,9 @@ this.chart.destroy() } - this.$http.get('${url('{}.graph_readings'.format(route_prefix), uuid=probe.uuid)}', {params: {'time-range': timeRange}}).then(({ data }) => { + let url = '${url(f'{route_prefix}.graph_readings', uuid=probe.uuid)}' + let params = {'time-range': timeRange} + this.$http.get(url, {params: params}).then(({ data }) => { this.chart = new Chart(this.$refs.tempchart, { type: 'scatter', diff --git a/tailbone/views/tempmon/probes.py b/tailbone/views/tempmon/probes.py index 6d12a3d2..381a9f4a 100644 --- a/tailbone/views/tempmon/probes.py +++ b/tailbone/views/tempmon/probes.py @@ -26,12 +26,11 @@ Views for tempmon probes import datetime -from rattail.time import make_utc, localtime from rattail_tempmon.db import model as tempmon import colander from deform import widget as dfwidget -from webhelpers2.html import tags, HTML +from webhelpers2.html import tags from tailbone import forms, grids from tailbone.views.tempmon import MasterView @@ -258,27 +257,16 @@ class TempmonProbeView(MasterView): selected = self.request.session.get(key, 'last hour') self.request.session[key] = selected - range_options = tags.Options([ - tags.Option("Last Hour", 'last hour'), - tags.Option("Last 6 Hours", 'last 6 hours'), - tags.Option("Last Day", 'last day'), - tags.Option("Last Week", 'last week'), - ]) - - time_range = HTML.tag('b-select', c=[range_options.render()], - **{'v-model': 'currentTimeRange', - '@input': 'timeRangeChanged'}) - context = { 'probe': probe, 'parent_title': str(probe), 'parent_url': self.get_action_url('view', probe), - 'time_range': time_range, 'current_time_range': selected, } return self.render_to_response('graph', context) def graph_readings(self): + app = self.get_rattail_app() probe = self.get_instance() key = 'tempmon.probe.{}.graph_time_range'.format(probe.uuid) @@ -299,7 +287,7 @@ class TempmonProbeView(MasterView): raise NotImplementedError("Unknown time range: {}".format(selected)) # figure out which readings we need to graph - cutoff = make_utc() - datetime.timedelta(seconds=cutoff) + cutoff = app.make_utc() - datetime.timedelta(seconds=cutoff) readings = self.Session.query(tempmon.Reading)\ .filter(tempmon.Reading.probe == probe)\ .filter(tempmon.Reading.taken >= cutoff)\ @@ -308,7 +296,7 @@ class TempmonProbeView(MasterView): # convert readings to data for scatter plot data = [{ - 'x': localtime(self.rattail_config, reading.taken, from_utc=True).isoformat(), + 'x': app.localtime(reading.taken, from_utc=True).isoformat(), 'y': float(reading.degrees_f), } for reading in readings] return data From 1f3b5a49c4b6789198c6f06fe86cacba943d7c0b Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 15 Jul 2023 19:32:04 -0500 Subject: [PATCH 010/542] 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 727bd250..eeec3c57 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ CHANGELOG ========= +0.9.39 (2023-07-15) +------------------- + +* Show invoice number for each row in receiving. + +* Tweak display options for tempmon probe readings graph. + + 0.9.38 (2023-07-07) ------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index aa6a7d76..48ba66dd 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.9.38' +__version__ = '0.9.39' From 9f0cfc68c1f886de4c877dadc9982a0057a363a4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 2 Aug 2023 21:59:52 -0500 Subject: [PATCH 011/542] Make system key searchable for problem report grid --- tailbone/views/reports.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tailbone/views/reports.py b/tailbone/views/reports.py index a1c737b6..5a945f0c 100644 --- a/tailbone/views/reports.py +++ b/tailbone/views/reports.py @@ -629,7 +629,9 @@ class ProblemReportView(MasterView): return data def configure_grid(self, g): - super(ProblemReportView, self).configure_grid(g) + super().configure_grid(g) + + g.set_searchable('system_key') g.set_renderer('email_recipients', self.render_email_recipients) From ec7b0cdda178a68b656838d146a86824d3ac1a24 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 3 Aug 2023 22:42:34 -0500 Subject: [PATCH 012/542] 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 eeec3c57..08bff3b8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGELOG ========= +0.9.40 (2023-08-03) +------------------- + +* Make system key searchable for problem report grid. + + 0.9.39 (2023-07-15) ------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index 48ba66dd..6d32d447 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.9.39' +__version__ = '0.9.40' From d504da19c5197ee0d6d2c37c09bbc1e94470f264 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 7 Aug 2023 12:36:07 -0500 Subject: [PATCH 013/542] Add common logic to validate employee reference field --- tailbone/views/master.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tailbone/views/master.py b/tailbone/views/master.py index e0c42e6e..eeae4dae 100644 --- a/tailbone/views/master.py +++ b/tailbone/views/master.py @@ -856,6 +856,13 @@ class MasterView(View): url = self.request.route_url('stores.view', uuid=store.uuid) return tags.link_to(text, url) + def valid_employee_uuid(self, node, value): + if value: + model = self.model + employee = self.Session.get(model.Employee, value) + if not employee: + node.raise_invalid("Employee not found") + def render_product(self, obj, field): product = getattr(obj, field) if not product: From f2915afda4dd94ad95facd4c23e2f100e9e94909 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 8 Aug 2023 14:11:54 -0500 Subject: [PATCH 014/542] Fix HTML rendering for UOM choice options also avoid deprecated config methods --- tailbone/templates/deform/select_dynamic.pt | 2 +- tailbone/views/custorders/orders.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tailbone/templates/deform/select_dynamic.pt b/tailbone/templates/deform/select_dynamic.pt index a0ee1daf..712830d1 100644 --- a/tailbone/templates/deform/select_dynamic.pt +++ b/tailbone/templates/deform/select_dynamic.pt @@ -26,7 +26,7 @@ diff --git a/tailbone/views/custorders/orders.py b/tailbone/views/custorders/orders.py index 563739ea..cdf765a6 100644 --- a/tailbone/views/custorders/orders.py +++ b/tailbone/views/custorders/orders.py @@ -341,7 +341,7 @@ class CustomerOrderView(MasterView): 'allow_contact_info_choice': self.batch_handler.allow_contact_info_choice(), 'allow_contact_info_create': self.batch_handler.allow_contact_info_creation(), 'order_items': items, - 'product_key_label': self.rattail_config.product_key_title(), + 'product_key_label': app.get_product_key_label(), 'allow_unknown_product': self.batch_handler.allow_unknown_product(), 'department_options': self.get_department_options(), 'default_uom_choices': self.batch_handler.uom_choices_for_product(None), @@ -767,7 +767,7 @@ class CustomerOrderView(MasterView): if self.batch_handler.product_price_may_be_questionable(): data['price_needs_confirmation'] = row.price_needs_confirmation - key = self.rattail_config.product_key() + key = app.get_product_key_field() if key == 'upc': data['product_key'] = data['product_upc_pretty'] elif key == 'item_id': From 845b5cda1a6730fe27028d180b246e2c221b3f40 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 8 Aug 2023 18:06:22 -0500 Subject: [PATCH 015/542] Fix custom cell click handlers in main buefy grid tables just used for editing catalog/invoice cost in receiving thus far.. --- tailbone/templates/grids/buefy.mako | 20 +++++++++++++++++--- tailbone/templates/receiving/view.mako | 3 ++- tailbone/views/purchasing/receiving.py | 9 +++------ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/tailbone/templates/grids/buefy.mako b/tailbone/templates/grids/buefy.mako index d96358d5..42451597 100644 --- a/tailbone/templates/grids/buefy.mako +++ b/tailbone/templates/grids/buefy.mako @@ -180,18 +180,21 @@ % endif :checkable="checkable" + % if grid.checkboxes: :checked-rows.sync="checkedRows" % if grid.clicking_row_checks_box: @click="rowClick" % endif % endif + % if grid.check_handler: @check="${grid.check_handler}" % endif % if grid.check_all_handler: @check-all="${grid.check_all_handler}" % endif + % if isinstance(grid.checkable, str): :is-row-checkable="${grid.row_checkable}" % elif grid.checkable: @@ -204,6 +207,10 @@ @sort="onSort" % endif + % if grid.click_handlers: + @cellclick="cellClick" + % endif + :paginated="paginated" :per-page="perPage" :current-page="currentPage" @@ -227,9 +234,6 @@ searchable % endif cell-class="c_${column['field']}" - % if grid.has_click_handler(column['field']): - @click.native="${grid.click_handlers[column['field']]}" - % endif :visible="${json.dumps(column['visible'])}"> % if column['field'] in grid.raw_renderers: ${grid.raw_renderers[column['field']]()} @@ -392,6 +396,16 @@ methods: { + % if grid.click_handlers: + cellClick(row, column, rowIndex, columnIndex) { + % for key in grid.click_handlers: + if (column._props.field == '${key}') { + ${grid.click_handlers[key]}(row) + } + % endfor + }, + % endif + copyDirectLink() { if (navigator.clipboard) { diff --git a/tailbone/templates/receiving/view.mako b/tailbone/templates/receiving/view.mako index 463fdf6c..b4de37f1 100644 --- a/tailbone/templates/receiving/view.mako +++ b/tailbone/templates/receiving/view.mako @@ -103,7 +103,8 @@ ref="input" v-show="editing" @keydown.native="inputKeyDown" - @blur="inputBlur"> + @blur="inputBlur" + style="width: 6rem;">
diff --git a/tailbone/views/purchasing/receiving.py b/tailbone/views/purchasing/receiving.py index d4bed60a..35e1d6b4 100644 --- a/tailbone/views/purchasing/receiving.py +++ b/tailbone/views/purchasing/receiving.py @@ -968,19 +968,16 @@ class ReceivingBatchView(PurchasingBatchView): g.filters['vendor_code'].default_verb = 'contains' # catalog_unit_cost - if (self.handler.has_purchase_order(batch) - or self.handler.has_invoice_file(batch)): - g.remove('catalog_unit_cost') - elif self.allow_edit_catalog_unit_cost(batch): + if self.allow_edit_catalog_unit_cost(batch): g.set_raw_renderer('catalog_unit_cost', self.render_catalog_unit_cost) g.set_click_handler('catalog_unit_cost', - 'catalogUnitCostClicked(props.row)') + 'this.catalogUnitCostClicked') # invoice_unit_cost if self.allow_edit_invoice_unit_cost(batch): g.set_raw_renderer('invoice_unit_cost', self.render_invoice_unit_cost) g.set_click_handler('invoice_unit_cost', - 'invoiceUnitCostClicked(props.row)') + 'this.invoiceUnitCostClicked') # nb. only show PO *or* invoice cost; prefer the latter unless # we have a PO and no invoice From 4ecea891b3347a878b710569e3a07c120a5a922a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 8 Aug 2023 18:42:50 -0500 Subject: [PATCH 016/542] 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 08bff3b8..f43e669b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,16 @@ CHANGELOG ========= +0.9.41 (2023-08-08) +------------------- + +* Add common logic to validate employee reference field. + +* Fix HTML rendering for UOM choice options. + +* Fix custom cell click handlers in main buefy grid tables. + + 0.9.40 (2023-08-03) ------------------- diff --git a/tailbone/_version.py b/tailbone/_version.py index 6d32d447..07ccc0e9 100644 --- a/tailbone/_version.py +++ b/tailbone/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.9.40' +__version__ = '0.9.41' From 90075b3b6539d554ccca6915fe6fcab14b7df7fe Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 9 Aug 2023 18:04:51 -0500 Subject: [PATCH 017/542] When bulk-deleting, skip objects which are not "deletable" whatever that means in context --- tailbone/views/master.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tailbone/views/master.py b/tailbone/views/master.py index eeae4dae..107870cd 100644 --- a/tailbone/views/master.py +++ b/tailbone/views/master.py @@ -1728,7 +1728,8 @@ class MasterView(View): def bulk_delete_objects(self, session, objects, progress=None): def delete(obj, i): - self.delete_instance(obj) + if self.deletable_instance(obj): + self.delete_instance(obj) if i % 1000 == 0: session.flush() From a007606863ab386578018c765a388f50a9bf8d0f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 17 Aug 2023 18:12:42 -0500 Subject: [PATCH 018/542] Declare "from PO" receiving workflow if applicable, in API --- tailbone/api/batch/receiving.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tailbone/api/batch/receiving.py b/tailbone/api/batch/receiving.py index 9a6864db..b02215d2 100644 --- a/tailbone/api/batch/receiving.py +++ b/tailbone/api/batch/receiving.py @@ -77,9 +77,15 @@ class ReceivingBatchViews(APIBatchView): def create_object(self, data): data = dict(data) + + # all about receiving mode here data['mode'] = self.enum.PURCHASE_BATCH_MODE_RECEIVING - batch = super(ReceivingBatchViews, self).create_object(data) - return batch + + # assume "receive from PO" if given a PO key + if data['purchase_key']: + data['receiving_workflow'] = 'from_po' + + return super().create_object(data) def auto_receive(self): """ From b2aea57da6933d84b79d049f10c07dff20d56579 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 18 Aug 2023 15:04:52 -0500 Subject: [PATCH 019/542] Auto-select text when editing costs for receiving --- tailbone/templates/receiving/view.mako | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tailbone/templates/receiving/view.mako b/tailbone/templates/receiving/view.mako index b4de37f1..77560ac1 100644 --- a/tailbone/templates/receiving/view.mako +++ b/tailbone/templates/receiving/view.mako @@ -103,6 +103,7 @@ ref="input" v-show="editing" @keydown.native="inputKeyDown" + @focus="selectAll" @blur="inputBlur" style="width: 6rem;"> @@ -189,6 +190,12 @@ }, methods: { + selectAll() { + // nb. must traverse into the element + let trueInput = this.$refs.input.$el.firstChild + trueInput.select() + }, + startEdit() { this.inputValue = this.value this.editing = true From 8be7dac33b7020b3ae59db15ace1c74a9b9524cb Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 24 Aug 2023 22:00:11 -0500 Subject: [PATCH 020/542] Include shopper history from parent customer account perspective ..right? or should this be hidden? configurable etc.? --- tailbone/views/people.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tailbone/views/people.py b/tailbone/views/people.py index 8dc96037..54d00ca7 100644 --- a/tailbone/views/people.py +++ b/tailbone/views/people.py @@ -1283,6 +1283,22 @@ class PersonView(MasterView): .filter(cls.account_holder_uuid == person.uuid) versions.extend(query.all()) + # CustomerShopper (from Customer perspective) + cls = continuum.version_class(model.CustomerShopper) + query = self.Session.query(cls)\ + .join(model.Customer, model.Customer.uuid == cls.customer_uuid)\ + .filter(model.Customer.account_holder_uuid == person.uuid) + versions.extend(query.all()) + + # CustomerShopperHistory (from Customer perspective) + cls = continuum.version_class(model.CustomerShopperHistory) + query = self.Session.query(cls)\ + .join(model.CustomerShopper, + model.CustomerShopper.uuid == cls.shopper_uuid)\ + .join(model.Customer)\ + .filter(model.Customer.account_holder_uuid == person.uuid) + versions.extend(query.all()) + # CustomerShopper (from Shopper perspective) cls = continuum.version_class(model.CustomerShopper) query = self.Session.query(cls)\ From bc8b5a8d324b3d30410ef8222e068714cdb7b84a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 25 Aug 2023 09:08:33 -0500 Subject: [PATCH 021/542] Link to product record, for New Product batch row also fix a typo --- tailbone/templates/products/configure.mako | 2 +- tailbone/views/batch/newproduct.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tailbone/templates/products/configure.mako b/tailbone/templates/products/configure.mako index a8caeac7..10f3c0e5 100644 --- a/tailbone/templates/products/configure.mako +++ b/tailbone/templates/products/configure.mako @@ -50,7 +50,7 @@

Handling

- + Date: Fri, 25 Aug 2023 10:41:20 -0500 Subject: [PATCH 022/542] Fix profile history to show when a CustomerShopperHistory is deleted --- tailbone/views/people.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tailbone/views/people.py b/tailbone/views/people.py index 54d00ca7..48391f63 100644 --- a/tailbone/views/people.py +++ b/tailbone/views/people.py @@ -1307,10 +1307,10 @@ class PersonView(MasterView): # CustomerShopperHistory (from Shopper perspective) cls = continuum.version_class(model.CustomerShopperHistory) + standin = continuum.version_class(model.CustomerShopper) query = self.Session.query(cls)\ - .join(model.CustomerShopper, - model.CustomerShopper.uuid == cls.shopper_uuid)\ - .filter(model.CustomerShopper.person_uuid == person.uuid) + .join(standin, standin.uuid == cls.shopper_uuid)\ + .filter(standin.person_uuid == person.uuid) versions.extend(query.all()) # PersonNote From 844c629a6a013ce57ff01f896fa9cce442cd6426 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 25 Aug 2023 13:59:58 -0500 Subject: [PATCH 023/542] Fix profile history to show when a CustomerShopperHistory is deleted --- tailbone/views/people.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tailbone/views/people.py b/tailbone/views/people.py index 48391f63..d7f84849 100644 --- a/tailbone/views/people.py +++ b/tailbone/views/people.py @@ -1292,10 +1292,10 @@ class PersonView(MasterView): # CustomerShopperHistory (from Customer perspective) cls = continuum.version_class(model.CustomerShopperHistory) + standin = continuum.version_class(model.CustomerShopper) query = self.Session.query(cls)\ - .join(model.CustomerShopper, - model.CustomerShopper.uuid == cls.shopper_uuid)\ - .join(model.Customer)\ + .join(standin, standin.uuid == cls.shopper_uuid)\ + .join(model.Customer, model.Customer.uuid == standin.customer_uuid)\ .filter(model.Customer.account_holder_uuid == person.uuid) versions.extend(query.all()) From 12e477909305a1f2ed4b7e4ba2b421ab727c782e Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 28 Aug 2023 20:43:31 -0500 Subject: [PATCH 024/542] Fairly massive overhaul of the Profile view; standardize tabs etc. much cleaner and more consistent interface now, between the main ProfileInfo component, and various *Tab components also cleaner interface between client-side JS and server view methods to my knowledge this is complete and breaks nothing..we'll see! --- tailbone/templates/members/configure.mako | 14 + tailbone/templates/page.mako | 7 +- .../templates/people/view_profile_buefy.mako | 1830 +++++++++-------- tailbone/views/members.py | 5 + tailbone/views/people.py | 397 ++-- 5 files changed, 1234 insertions(+), 1019 deletions(-) diff --git a/tailbone/templates/members/configure.mako b/tailbone/templates/members/configure.mako index c0e0355d..465bf611 100644 --- a/tailbone/templates/members/configure.mako +++ b/tailbone/templates/members/configure.mako @@ -36,6 +36,20 @@
+ +

Relationships

+
+ + + + Limit one (1) Member account per Person + + + +
<%def name="modify_this_page_vars()"> diff --git a/tailbone/templates/page.mako b/tailbone/templates/page.mako index b5ac8773..bf799440 100644 --- a/tailbone/templates/page.mako +++ b/tailbone/templates/page.mako @@ -38,7 +38,12 @@ }, computed: {}, watch: {}, - methods: {}, + methods: { + + changeContentTitle(newTitle) { + this.$emit('change-content-title', newTitle) + }, + }, } let ThisPageData = { diff --git a/tailbone/templates/people/view_profile_buefy.mako b/tailbone/templates/people/view_profile_buefy.mako index e1da8661..5574088e 100644 --- a/tailbone/templates/people/view_profile_buefy.mako +++ b/tailbone/templates/people/view_profile_buefy.mako @@ -119,17 +119,17 @@