Initial support for adding items to, executing customer order batch
This commit is contained in:
parent
475ab3013f
commit
480d878db8
6 changed files with 701 additions and 37 deletions
|
@ -22,24 +22,32 @@
|
|||
</%def>
|
||||
|
||||
<%def name="order_form_buttons()">
|
||||
<div class="buttons">
|
||||
<b-button type="is-primary"
|
||||
@click="submitOrder()"
|
||||
icon-pack="fas"
|
||||
icon-left="fas fa-upload">
|
||||
Submit this Order
|
||||
</b-button>
|
||||
<b-button @click="startOverEntirely()"
|
||||
icon-pack="fas"
|
||||
icon-left="fas fa-redo">
|
||||
Start Over Entirely
|
||||
</b-button>
|
||||
<b-button @click="cancelOrder()"
|
||||
type="is-danger"
|
||||
icon-pack="fas"
|
||||
icon-left="fas fa-trash">
|
||||
Cancel this Order
|
||||
</b-button>
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<div class="buttons">
|
||||
<b-button type="is-primary"
|
||||
@click="submitOrder()"
|
||||
icon-pack="fas"
|
||||
icon-left="fas fa-upload">
|
||||
Submit this Order
|
||||
</b-button>
|
||||
<b-button @click="startOverEntirely()"
|
||||
icon-pack="fas"
|
||||
icon-left="fas fa-redo">
|
||||
Start Over Entirely
|
||||
</b-button>
|
||||
<b-button @click="cancelOrder()"
|
||||
type="is-danger"
|
||||
icon-pack="fas"
|
||||
icon-left="fas fa-trash">
|
||||
Cancel this Order
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
|
@ -163,12 +171,157 @@
|
|||
## (for now we just always show caret-right instead)
|
||||
icon="caret-right">
|
||||
</b-icon>
|
||||
<strong>Items</strong>
|
||||
<strong v-html="itemsPanelHeader"></strong>
|
||||
</div>
|
||||
|
||||
<div class="panel-block">
|
||||
<div>
|
||||
TODO: items go here
|
||||
<b-button type="is-primary"
|
||||
icon-pack="fas"
|
||||
icon-left="fas fa-plus"
|
||||
@click="showAddItemDialog()">
|
||||
Add Item
|
||||
</b-button>
|
||||
<b-modal :active.sync="showingItemDialog">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
|
||||
<b-tabs type="is-boxed is-toggle"
|
||||
:animated="false">
|
||||
|
||||
<b-tab-item label="Product">
|
||||
|
||||
<div class="field">
|
||||
<b-radio v-model="productIsKnown"
|
||||
:native-value="true">
|
||||
Product is already in the system.
|
||||
</b-radio>
|
||||
</div>
|
||||
|
||||
<div v-show="productIsKnown">
|
||||
|
||||
<b-field grouped>
|
||||
<b-field label="Description" horizontal expanded>
|
||||
<tailbone-autocomplete
|
||||
ref="productDescriptionAutocomplete"
|
||||
v-model="productUUID"
|
||||
:assigned-value="productUUID"
|
||||
:assigned-label="productDisplay"
|
||||
serviceUrl="${url('products.autocomplete')}"
|
||||
@input="productChanged">
|
||||
</tailbone-autocomplete>
|
||||
</b-field>
|
||||
</b-field>
|
||||
|
||||
<b-field grouped>
|
||||
<b-field label="UPC" horizontal expanded>
|
||||
<b-input v-if="!productUUID"
|
||||
v-model="productUPC"
|
||||
ref="productUPCInput">
|
||||
</b-input>
|
||||
<b-button v-if="!productUUID"
|
||||
@click="fetchProductByUPC()">
|
||||
Fetch
|
||||
</b-button>
|
||||
<b-button v-if="productUUID"
|
||||
@click="clearProduct(true)">
|
||||
{{ productUPC }} (click to change)
|
||||
</b-button>
|
||||
</b-field>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<b-radio v-model="productIsKnown" disabled
|
||||
:native-value="false">
|
||||
Product is not yet in the system.
|
||||
</b-radio>
|
||||
</div>
|
||||
|
||||
</b-tab-item>
|
||||
<b-tab-item label="Quantity">
|
||||
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="Quantity" horizontal>
|
||||
<b-input v-model="productQuantity"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-select v-model="productUOM">
|
||||
<option v-for="choice in productUnitChoices"
|
||||
:key="choice.key"
|
||||
:value="choice.key"
|
||||
v-html="choice.value">
|
||||
</option>
|
||||
</b-select>
|
||||
|
||||
</b-field>
|
||||
</b-tab-item>
|
||||
</b-tabs>
|
||||
|
||||
<div class="buttons">
|
||||
<b-button @click="showingItemDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
<b-button type="is-primary"
|
||||
icon-pack="fas"
|
||||
icon-left="fas fa-save"
|
||||
@click="itemDialogSave()">
|
||||
{{ itemDialogSaveButtonText }}
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
|
||||
<b-table
|
||||
:data="items">
|
||||
<template slot-scope="props">
|
||||
|
||||
<b-table-column field="product_upc_pretty" label="UPC">
|
||||
{{ props.row.product_upc_pretty }}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="product_brand" label="Brand">
|
||||
{{ props.row.product_brand }}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="product_description" label="Description">
|
||||
{{ props.row.product_description }}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="product_size" label="Size">
|
||||
{{ props.row.product_size }}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="order_quantity_display" label="Quantity">
|
||||
<span v-html="props.row.order_quantity_display"></span>
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="total_price_display" label="Total">
|
||||
{{ props.row.total_price_display }}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="actions" label="Actions">
|
||||
<a href="#" class="grid-action"
|
||||
@click.prevent="showEditItemDialog(props.index)">
|
||||
<i class="fas fa-edit"></i>
|
||||
Edit
|
||||
</a>
|
||||
|
||||
|
||||
<a href="#" class="grid-action has-text-danger"
|
||||
@click.prevent="deleteItem(props.index)">
|
||||
<i class="fas fa-trash"></i>
|
||||
Delete
|
||||
</a>
|
||||
|
||||
</b-table-column>
|
||||
|
||||
</template>
|
||||
</b-table>
|
||||
</div>
|
||||
</div>
|
||||
</b-collapse>
|
||||
|
@ -191,10 +344,20 @@
|
|||
const CustomerOrderCreator = {
|
||||
template: '#customer-order-creator-template',
|
||||
data() {
|
||||
|
||||
## TODO: these should come from handler
|
||||
let defaultUnitChoices = [
|
||||
{key: '${enum.UNIT_OF_MEASURE_EACH}', value: "Each"},
|
||||
{key: '${enum.UNIT_OF_MEASURE_POUND}', value: "Pound"},
|
||||
{key: '${enum.UNIT_OF_MEASURE_CASE}', value: "Case"},
|
||||
]
|
||||
let defaultUOM = '${enum.UNIT_OF_MEASURE_CASE}'
|
||||
|
||||
return {
|
||||
batchAction: null,
|
||||
batchTotalPriceDisplay: ${json.dumps(normalized_batch['total_price_display'])|n},
|
||||
|
||||
customerPanelOpen: true,
|
||||
customerPanelOpen: false,
|
||||
customerIsKnown: true,
|
||||
customerUUID: ${json.dumps(batch.customer_uuid)|n},
|
||||
customerDisplay: ${json.dumps(six.text_type(batch.customer or ''))|n},
|
||||
|
@ -204,6 +367,20 @@
|
|||
customerName: null,
|
||||
phoneNumber: null,
|
||||
|
||||
items: ${json.dumps(order_items)|n},
|
||||
editingItem: null,
|
||||
showingItemDialog: false,
|
||||
productIsKnown: true,
|
||||
productUUID: null,
|
||||
productDisplay: null,
|
||||
productUPC: null,
|
||||
productQuantity: null,
|
||||
defaultUnitChoices: defaultUnitChoices,
|
||||
productUnitChoices: defaultUnitChoices,
|
||||
defaultUOM: defaultUOM,
|
||||
productUOM: defaultUOM,
|
||||
productCaseSize: null,
|
||||
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||
}
|
||||
|
@ -301,9 +478,28 @@
|
|||
|
||||
return {
|
||||
type: null,
|
||||
text: "Everything seems to be okay here.",
|
||||
text: "Customer info looks okay.",
|
||||
}
|
||||
},
|
||||
|
||||
itemsPanelHeader() {
|
||||
let text = "Items"
|
||||
|
||||
if (this.items.length) {
|
||||
text = "Items: " + this.items.length.toString() + " for " + this.batchTotalPriceDisplay
|
||||
}
|
||||
|
||||
return text
|
||||
},
|
||||
|
||||
itemDialogSaveButtonText() {
|
||||
return this.editingItem ? "Update Item" : "Add Item"
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.customerStatusType) {
|
||||
this.customerPanelOpen = true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
|
@ -369,6 +565,12 @@
|
|||
if (callback) {
|
||||
callback(response)
|
||||
}
|
||||
}, response => {
|
||||
this.$buefy.toast.open({
|
||||
message: "Unexpected error occurred",
|
||||
type: 'is-danger',
|
||||
duration: 2000, // 2 seconds
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -385,7 +587,24 @@
|
|||
},
|
||||
|
||||
submitOrder() {
|
||||
alert("okay then!")
|
||||
let params = {
|
||||
action: 'submit_new_order',
|
||||
}
|
||||
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
|
||||
})
|
||||
} else {
|
||||
if (response.data.next_url) {
|
||||
location.href = response.data.next_url
|
||||
} else {
|
||||
location.reload()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
customerChanged(uuid) {
|
||||
|
@ -414,6 +633,155 @@
|
|||
this.setCustomerData()
|
||||
}
|
||||
},
|
||||
|
||||
showAddItemDialog() {
|
||||
this.editingItem = null
|
||||
this.productIsKnown = true
|
||||
this.productUUID = null
|
||||
this.productDisplay = null
|
||||
this.productUPC = null
|
||||
this.productQuantity = 1
|
||||
this.productUnitChoices = this.defaultUnitChoices
|
||||
this.productUOM = this.defaultUOM
|
||||
this.showingItemDialog = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.productDescriptionAutocomplete.focus()
|
||||
})
|
||||
},
|
||||
|
||||
showEditItemDialog(index) {
|
||||
row = this.items[index]
|
||||
this.editingItem = row
|
||||
this.productIsKnown = true // TODO
|
||||
this.productUUID = row.product_uuid
|
||||
this.productDisplay = row.product_full_description
|
||||
this.productUPC = row.product_upc_pretty || row.product_upc
|
||||
this.productQuantity = row.order_quantity
|
||||
this.productUnitChoices = row.order_uom_choices
|
||||
this.productUOM = row.order_uom
|
||||
|
||||
this.showingItemDialog = true
|
||||
},
|
||||
|
||||
deleteItem(index) {
|
||||
if (!confirm("Are you sure you want to delete this item?")) {
|
||||
return
|
||||
}
|
||||
|
||||
let params = {
|
||||
action: 'delete_item',
|
||||
uuid: this.items[index].uuid,
|
||||
}
|
||||
this.submitBatchData(params, response => {
|
||||
if (response.data.error) {
|
||||
this.$buefy.toast.open({
|
||||
message: "Delete failed: " + response.data.error,
|
||||
type: 'is-warning',
|
||||
duration: 2000, // 2 seconds
|
||||
})
|
||||
} else {
|
||||
this.items.splice(index, 1)
|
||||
this.batchTotalPriceDisplay = response.data.batch.total_price_display
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
clearProduct(autofocus) {
|
||||
this.productUUID = null
|
||||
this.productDisplay = null
|
||||
this.productUPC = null
|
||||
this.productUnitChoices = this.defaultUnitChoices
|
||||
if (autofocus) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.productUPCInput.focus()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
fetchProductByUPC() {
|
||||
let params = {
|
||||
action: 'find_product_by_upc',
|
||||
upc: this.productUPC,
|
||||
}
|
||||
this.submitBatchData(params, response => {
|
||||
if (response.data.error) {
|
||||
this.$buefy.toast.open({
|
||||
message: "Fetch failed: " + response.data.error,
|
||||
type: 'is-warning',
|
||||
duration: 2000, // 2 seconds
|
||||
})
|
||||
} else {
|
||||
this.productUUID = response.data.uuid
|
||||
this.productUPC = response.data.upc_pretty
|
||||
this.productDisplay = response.data.full_description
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
productChanged(uuid) {
|
||||
if (uuid) {
|
||||
this.productUUID = uuid
|
||||
let params = {
|
||||
action: 'get_product_info',
|
||||
uuid: this.productUUID,
|
||||
}
|
||||
this.submitBatchData(params, response => {
|
||||
this.productUPC = response.data.upc_pretty
|
||||
this.productDisplay = response.data.full_description
|
||||
this.productUnitChoices = response.data.uom_choices
|
||||
|
||||
let found = false
|
||||
for (let uom of this.productUnitChoices) {
|
||||
if (this.productUOM == uom.key) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
this.productUOM = this.productUnitChoices[0].key
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.clearProduct()
|
||||
}
|
||||
},
|
||||
|
||||
itemDialogSave() {
|
||||
|
||||
let params = {
|
||||
product_is_known: this.productIsKnown,
|
||||
product_uuid: this.productUUID,
|
||||
order_quantity: this.productQuantity,
|
||||
order_uom: this.productUOM,
|
||||
}
|
||||
|
||||
if (this.editingItem) {
|
||||
params.action = 'update_item'
|
||||
params.uuid = this.editingItem.uuid
|
||||
} else {
|
||||
params.action = 'add_item'
|
||||
}
|
||||
|
||||
this.submitBatchData(params, response => {
|
||||
|
||||
if (params.action == 'add_item') {
|
||||
this.items.push(response.data.row)
|
||||
|
||||
} else { // update_item
|
||||
// must update each value separately, instead of
|
||||
// overwriting the item record, or else display will
|
||||
// not update properly
|
||||
for (let [key, value] of Object.entries(response.data.row)) {
|
||||
this.editingItem[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// also update the batch total price
|
||||
this.batchTotalPriceDisplay = response.data.batch.total_price_display
|
||||
|
||||
this.showingItemDialog = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue