diff --git a/tailbone/views/purchases/core.py b/tailbone/views/purchases/core.py index ee532fcd..6fae86fd 100644 --- a/tailbone/views/purchases/core.py +++ b/tailbone/views/purchases/core.py @@ -310,6 +310,17 @@ class PurchaseView(MasterView): # department f.set_renderer('department', self.render_row_department) + # product + f.set_renderer('product', self.render_row_product) + + def render_row_product(self, row, field): + product = row.product + if not product: + return "" + text = six.text_type(product) + url = self.request.route_url('products.view', uuid=product.uuid) + return tags.link_to(text, url) + def render_row_department(self, row, field): return "{} {}".format(row.department_number, row.department_name) diff --git a/tailbone/views/purchasing/batch.py b/tailbone/views/purchasing/batch.py index fa73b96f..805dc407 100644 --- a/tailbone/views/purchasing/batch.py +++ b/tailbone/views/purchasing/batch.py @@ -221,15 +221,24 @@ class PurchasingBatchView(BatchMasterView): # mode f.set_enum('mode', self.enum.PURCHASE_BATCH_MODE) - # TODO: this hardly seems complete... # store + single_store = self.rattail_config.single_store() if self.creating: f.replace('store', 'store_uuid') - f.set_widget('store_uuid', dfwidget.SelectWidget(values=self.get_store_values())) - f.set_label('store_uuid', "Store") + if single_store: + store = self.rattail_config.get_store(self.Session()) + f.set_widget('store_uuid', forms.widgets.ReadonlyWidget()) + f.set_default('store_uuid', store.uuid) + f.set_hidden('store_uuid') + else: + f.set_widget('store_uuid', dfwidget.SelectWidget(values=self.get_store_values())) + f.set_label('store_uuid', "Store") else: - f.set_readonly('store') - f.set_renderer('store', self.render_store) + if single_store: + f.remove_field('store') + else: + f.set_readonly('store') + f.set_renderer('store', self.render_store) # purchase f.set_renderer('purchase', self.render_purchase) @@ -243,17 +252,27 @@ class PurchasingBatchView(BatchMasterView): f.set_renderer('vendor', self.render_vendor) if self.creating: f.replace('vendor', 'vendor_uuid') - f.set_node('vendor_uuid', colander.String()) - vendor_display = "" - if self.request.method == 'POST': - if self.request.POST.get('vendor_uuid'): - vendor = self.Session.query(model.Vendor).get(self.request.POST['vendor_uuid']) - if vendor: - vendor_display = six.text_type(vendor) - vendors_url = self.request.route_url('vendors.autocomplete') - f.set_widget('vendor_uuid', forms.widgets.JQueryAutocompleteWidget( - field_display=vendor_display, service_url=vendors_url)) f.set_label('vendor_uuid', "Vendor") + widget_type = self.rattail_config.get('tailbone', 'default_widget.vendor', + default='autocomplete') + if widget_type == 'autocomplete': + vendor_display = "" + if self.request.method == 'POST': + if self.request.POST.get('vendor_uuid'): + vendor = self.Session.query(model.Vendor).get(self.request.POST['vendor_uuid']) + if vendor: + vendor_display = six.text_type(vendor) + vendors_url = self.request.route_url('vendors.autocomplete') + f.set_widget('vendor_uuid', forms.widgets.JQueryAutocompleteWidget( + field_display=vendor_display, service_url=vendors_url)) + elif widget_type == 'dropdown': + vendors = self.Session.query(model.Vendor)\ + .order_by(model.Vendor.id) + vendor_values = [(vendor.uuid, "({}) {}".format(vendor.id, vendor.name)) + for vendor in vendors] + f.set_widget('vendor_uuid', dfwidget.SelectWidget(values=vendor_values)) + else: + raise NotImplementedError("Unsupported vendor widget type: {}".format(widget_type)) elif self.editing: f.set_readonly('vendor') @@ -271,26 +290,27 @@ class PurchasingBatchView(BatchMasterView): f.set_readonly('department') # buyer - f.set_renderer('buyer', self.render_buyer) - if self.creating or self.editing: - f.replace('buyer', 'buyer_uuid') - f.set_node('buyer_uuid', colander.String(), missing=colander.null) - buyer_display = "" - if self.request.method == 'POST': - if self.request.POST.get('buyer_uuid'): - buyer = self.Session.query(model.Employee).get(self.request.POST['buyer_uuid']) - if buyer: - buyer_display = six.text_type(buyer) - elif self.creating: - buyer = self.request.user.employee - buyer_display = six.text_type(buyer) - f.set_default('buyer_uuid', buyer.uuid) - elif self.editing: - buyer_display = six.text_type(batch.buyer or '') - buyers_url = self.request.route_url('employees.autocomplete') - f.set_widget('buyer_uuid', forms.widgets.JQueryAutocompleteWidget( - field_display=buyer_display, service_url=buyers_url)) - f.set_label('buyer_uuid', "Buyer") + if 'buyer' in f: + f.set_renderer('buyer', self.render_buyer) + if self.creating or self.editing: + f.replace('buyer', 'buyer_uuid') + f.set_node('buyer_uuid', colander.String(), missing=colander.null) + buyer_display = "" + if self.request.method == 'POST': + if self.request.POST.get('buyer_uuid'): + buyer = self.Session.query(model.Employee).get(self.request.POST['buyer_uuid']) + if buyer: + buyer_display = six.text_type(buyer) + elif self.creating: + buyer = self.request.user.employee + buyer_display = six.text_type(buyer) + f.set_default('buyer_uuid', buyer.uuid) + elif self.editing: + buyer_display = six.text_type(batch.buyer or '') + buyers_url = self.request.route_url('employees.autocomplete') + f.set_widget('buyer_uuid', forms.widgets.JQueryAutocompleteWidget( + field_display=buyer_display, service_url=buyers_url)) + f.set_label('buyer_uuid', "Buyer") # date_ordered f.set_type('date_ordered', 'date_jquery') @@ -629,19 +649,33 @@ class PurchasingBatchView(BatchMasterView): elif self.editing: f.set_readonly('upc') + f.set_readonly('item_id') f.set_readonly('product') - f.remove_fields('po_total', - 'invoice_total', - 'status_code') + f.set_renderer('product', self.render_product) + + # TODO: what's up with this again? + # f.remove_fields('po_total', + # 'invoice_total', + # 'status_code') elif self.viewing: if row.product: f.remove_fields('brand_name', 'description', 'size') + f.set_renderer('product', self.render_row_product) else: f.remove_field('product') + + def render_row_product(self, row, field): + product = row.product + if not product: + return "" + text = six.text_type(product) + url = self.request.route_url('products.view', uuid=product.uuid) + return tags.link_to(text, url) + def configure_mobile_row_form(self, f): super(PurchasingBatchView, self).configure_mobile_row_form(f) # row = f.model_instance @@ -786,6 +820,11 @@ class PurchasingBatchView(BatchMasterView): # self.request.session.flash("Added item: {} {}".format(row.upc.pretty(), row.product)) # return self.redirect(self.request.current_route_url()) + # TODO: seems like this should be master behavior, controlled by setting? + def redirect_after_edit_row(self, row, mobile=False): + parent = self.get_parent(row) + return self.redirect(self.get_action_url('view', parent, mobile=mobile)) + def delete_row(self): """ Update the batch totals in addition to marking row as removed. diff --git a/tailbone/views/purchasing/receiving.py b/tailbone/views/purchasing/receiving.py index 97ff6cbe..6d5038ec 100644 --- a/tailbone/views/purchasing/receiving.py +++ b/tailbone/views/purchasing/receiving.py @@ -89,11 +89,38 @@ class ReceivingBatchView(PurchasingBatchView): model_title_plural = "Receiving Batches" index_title = "Receiving" creatable = False + rows_editable = True rows_deletable = False mobile_creatable = True mobile_rows_filterable = True mobile_rows_creatable = True + form_fields = [ + 'id', + 'store', + 'vendor', + 'department', + 'purchase', + 'vendor_email', + 'vendor_fax', + 'vendor_contact', + 'vendor_phone', + 'date_ordered', + 'date_received', + 'po_number', + 'po_total', + 'invoice_date', + 'invoice_number', + 'invoice_total', + 'notes', + 'created', + 'created_by', + 'status_code', + 'complete', + 'executed', + 'executed_by', + ] + mobile_form_fields = [ 'vendor', 'department', @@ -116,6 +143,34 @@ class ReceivingBatchView(PurchasingBatchView): 'status_code', ] + row_form_fields = [ + 'upc', + 'item_id', + 'product', + 'brand_name', + 'description', + 'size', + 'case_quantity', + 'cases_ordered', + 'units_ordered', + 'cases_received', + 'units_received', + 'cases_damaged', + 'units_damaged', + 'cases_expired', + 'units_expired', + 'cases_mispick', + 'units_mispick', + 'po_line_number', + 'po_unit_cost', + 'po_total', + 'invoice_line_number', + 'invoice_unit_cost', + 'invoice_total', + 'status_code', + 'credits', + ] + @property def batch_mode(self): return self.enum.PURCHASE_BATCH_MODE_RECEIVING @@ -177,8 +232,6 @@ class ReceivingBatchView(PurchasingBatchView): if mobile: purchase = self.get_purchase(self.request.POST['purchase']) - kwargs['sms_transaction_number'] = purchase.F1032 - numbers = [d.F03 for d in purchase.details] if numbers: number = max(set(numbers), key=numbers.count) @@ -186,8 +239,6 @@ class ReceivingBatchView(PurchasingBatchView): .filter(model.Department.number == number)\ .one() - else: - kwargs['sms_transaction_number'] = batch.sms_transaction_number return kwargs def configure_mobile_form(self, f): @@ -199,6 +250,14 @@ class ReceivingBatchView(PurchasingBatchView): # department # fs.department.with_renderer(fa.TextFieldRenderer), + def configure_row_form(self, f): + super(ReceivingBatchView, self).configure_row_form(f) + f.set_readonly('cases_ordered') + f.set_readonly('units_ordered') + f.set_readonly('po_unit_cost') + f.set_readonly('po_total') + f.set_readonly('invoice_total') + def render_mobile_row_listitem(self, row, i): description = row.product.full_description if row.product else row.description return "({}) {}".format(row.upc.pretty(), description)