diff --git a/tailbone/views/inventory.py b/tailbone/views/inventory.py index 83a2474d..e0e0976d 100644 --- a/tailbone/views/inventory.py +++ b/tailbone/views/inventory.py @@ -39,11 +39,12 @@ from rattail.util import pretty_quantity import colander import formencode as fe +from deform import widget as dfwidget from webhelpers2.html import HTML, tags -from tailbone import forms, grids +from tailbone import forms, forms2, grids from tailbone.views import MasterView4 as MasterView -from tailbone.views.batch import BatchMasterView3 as BatchMasterView +from tailbone.views.batch import BatchMasterView4 as BatchMasterView class InventoryAdjustmentReasonsView(MasterView): @@ -90,6 +91,10 @@ class InventoryBatchView(BatchMasterView): mobile_creatable = True mobile_rows_creatable = True + labels = { + 'mode': "Count Mode", + } + grid_columns = [ 'id', 'created', @@ -108,6 +113,16 @@ class InventoryBatchView(BatchMasterView): 'created', 'created_by', 'handheld_batches', + 'mode', + 'reason_code', + 'total_cost', + 'rowcount', + 'complete', + 'executed', + 'executed_by', + ] + + mobile_form_fields = [ 'mode', 'reason_code', 'rowcount', @@ -119,6 +134,11 @@ class InventoryBatchView(BatchMasterView): model_row_class = model.InventoryBatchRow rows_editable = True + row_labels = { + 'upc': "UPC", + 'previous_units_on_hand': "Prev. On Hand", + } + row_grid_columns = [ 'sequence', 'upc', @@ -134,6 +154,21 @@ class InventoryBatchView(BatchMasterView): 'status_code', ] + row_form_fields = [ + 'sequence', + 'upc', + 'brand_name', + 'description', + 'size', + 'status_code', + 'previous_units_on_hand', + 'case_quantity', + 'cases', + 'units', + 'unit_cost', + 'total_cost', + ] + def configure_grid(self, g): super(InventoryBatchView, self).configure_grid(g) @@ -141,7 +176,6 @@ class InventoryBatchView(BatchMasterView): g.set_enum('mode', self.enum.INVENTORY_MODE) g.filters['mode'].set_value_renderer( grids.filters.EnumValueRenderer(self.enum.INVENTORY_MODE)) - g.set_label('mode', "Count Mode") # total_cost g.set_type('total_cost', 'currency') @@ -162,10 +196,8 @@ class InventoryBatchView(BatchMasterView): def allow_worksheet(self, batch): return self.mutable_batch(batch) - def configure_form(self, f): - super(InventoryBatchView, self).configure_form(f) + def get_available_modes(self): permission_prefix = self.get_permission_prefix() - modes = dict(self.enum.INVENTORY_MODE) if not self.request.has_perm('{}.create.replace'.format(permission_prefix)): if hasattr(self.enum, 'INVENTORY_MODE_REPLACE'): @@ -175,16 +207,24 @@ class InventoryBatchView(BatchMasterView): if not self.request.has_perm('{}.create.zero'.format(permission_prefix)): if hasattr(self.enum, 'INVENTORY_MODE_ZERO_ALL'): modes.pop(self.enum.INVENTORY_MODE_ZERO_ALL, None) + return modes + + def configure_form(self, f): + super(InventoryBatchView, self).configure_form(f) # mode + modes = self.get_available_modes() f.set_enum('mode', modes) f.set_label('mode', "Count Mode") if len(modes) == 1: f.set_readonly('mode') # total_cost - f.set_readonly('total_cost') - f.set_type('total_cost', 'currency') + if self.creating: + f.remove_field('total_cost') + else: + f.set_readonly('total_cost') + f.set_type('total_cost', 'currency') # handheld_batches f.set_readonly('handheld_batches') @@ -205,7 +245,7 @@ class InventoryBatchView(BatchMasterView): return self.mutable_batch(row.batch) def save_edit_row_form(self, form): - row = form.fieldset.model + row = form.model_instance batch = row.batch if batch.total_cost is not None and row.total_cost is not None: batch.total_cost -= row.total_cost @@ -220,40 +260,19 @@ class InventoryBatchView(BatchMasterView): batch.total_cost -= row.total_cost return super(InventoryBatchView, self).delete_row() - def configure_mobile_fieldset(self, fs): - permission_prefix = self.get_permission_prefix() + def configure_mobile_form(self, f): + super(InventoryBatchView, self).configure_mobile_form(f) + batch = f.model_instance - # TODO: this was copied from configure_form() - modes = dict(self.enum.INVENTORY_MODE) - if not self.request.has_perm('{}.create.replace'.format(permission_prefix)): - if hasattr(self.enum, 'INVENTORY_MODE_REPLACE'): - modes.pop(self.enum.INVENTORY_MODE_REPLACE, None) - if hasattr(self.enum, 'INVENTORY_MODE_REPLACE_ADJUST'): - modes.pop(self.enum.INVENTORY_MODE_REPLACE_ADJUST, None) - if not self.request.has_perm('{}.create.zero'.format(permission_prefix)): - if hasattr(self.enum, 'INVENTORY_MODE_ZERO_ALL'): - modes.pop(self.enum.INVENTORY_MODE_ZERO_ALL, None) + # mode + modes = self.get_available_modes() + f.set_enum('mode', modes) + mode_values = [(k, v) for k, v in sorted(modes.items())] + f.set_widget('mode', forms2.widgets.PlainSelectWidget(values=mode_values)) - fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(modes), - label="Count Mode", required=True, attrs={'auto-enhance': 'true'}) - - fs.configure(include=[ - fs.mode, - fs.reason_code, - fs.rowcount, - fs.complete, - fs.executed, - fs.executed_by, - ]) - batch = fs.model - if self.creating: - del fs.rowcount - if not batch.executed: - del [fs.executed, fs.executed_by] - if not batch.complete: - del fs.complete - else: - del fs.complete + # complete + if self.creating or batch.executed or not batch.complete: + f.remove_field('complete') # TODO: document this, maybe move it etc. unknown_product_creates_row = True @@ -371,17 +390,20 @@ class InventoryBatchView(BatchMasterView): def configure_row_grid(self, g): super(InventoryBatchView, self).configure_row_grid(g) + # quantity fields g.set_type('previous_units_on_hand', 'quantity') g.set_type('cases', 'quantity') g.set_type('units', 'quantity') + + # currency fields g.set_type('unit_cost', 'currency') g.set_type('total_cost', 'currency') - g.set_label('upc', "UPC") + # short labels g.set_label('brand_name', "Brand") g.set_label('status_code', "Status") - g.set_label('previous_units_on_hand', "Prev. On Hand") + # links g.set_link('upc') g.set_link('item_id') g.set_link('description') @@ -396,37 +418,41 @@ class InventoryBatchView(BatchMasterView): qty = "{} {}".format(pretty_quantity(row.cases or row.units), 'CS' if row.cases else unit_uom) return "({}) {} - {}".format(row.upc.pretty(), description, qty) - def _preconfigure_row_fieldset(self, fs): - super(InventoryBatchView, self)._preconfigure_row_fieldset(fs) - fs.upc.set(readonly=True, label="UPC", renderer=forms.renderers.GPCFieldRenderer, - attrs={'link': lambda r: self.request.route_url('products.view', uuid=r.product_uuid)}) - fs.item_id.set(readonly=True) - fs.brand_name.set(readonly=True) - fs.description.set(readonly=True) - fs.size.set(readonly=True) - fs.previous_units_on_hand.set(label="Prev. On Hand") - fs.case_quantity.set(renderer=forms.renderers.QuantityFieldRenderer) - fs.cases.set(renderer=forms.renderers.QuantityFieldRenderer) - fs.units.set(renderer=forms.renderers.QuantityFieldRenderer) - fs.unit_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer) - fs.total_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer) + def configure_row_form(self, f): + super(InventoryBatchView, self).configure_row_form(f) - def configure_row_fieldset(self, fs): - fs.configure( - include=[ - fs.sequence, - fs.upc, - fs.brand_name, - fs.description, - fs.size, - fs.status_code, - fs.previous_units_on_hand, - fs.case_quantity, - fs.cases, - fs.units, - fs.unit_cost, - fs.total_cost, - ]) + # readonly fields + f.set_readonly('upc') + f.set_readonly('item_id') + f.set_readonly('brand_name') + f.set_readonly('description') + f.set_readonly('size') + f.set_readonly('previous_units_on_hand') + f.set_readonly('case_quantity') + f.set_readonly('unit_cost') + f.set_readonly('total_cost') + + # quantity fields + f.set_type('case_quantity', 'quantity') + f.set_type('cases', 'quantity') + f.set_type('units', 'quantity') + + # currency fields + f.set_type('unit_cost', 'currency') + f.set_type('total_cost', 'currency') + + # upc + f.set_renderer('upc', self.render_upc) + + def render_upc(self, row, field): + upc = row.upc + if not upc: + return "" + text = upc.pretty() + if row.product_uuid: + url = self.request.route_url('products.view', uuid=row.product_uuid) + return tags.link_to(text, url) + return text @classmethod def defaults(cls, config):