Compare commits

..

No commits in common. "master" and "v0.1.14" have entirely different histories.

18 changed files with 10521 additions and 24110 deletions

View file

@ -5,64 +5,6 @@ All notable changes to 'byjove' will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## Unreleased
## [0.1.26] - 2024-06-03
### Changed
- Optionally allow decimal quantities for receiving.
## [0.1.25] - 2024-05-29
### Changed
- Show actual error text if applicable, for receiving failure.
## [0.1.24] - 2024-03-26
### Changed
- Add delay when fetching rows data for model CRUD component.
## [0.1.23] - 2023-12-26
### Changed
- Improve focus behavior for inventory count view.
- Keep row filters "raw" until encoding for actual request.
## [0.1.22] - 2023-10-23
### Changed
- Use columns instead of table, for row receiving quantities.
- Show invoice number when receiving row.
- Center receiving buttons; add "12 EA" quick receive button.
## [0.1.21] - 2023-10-06
### Changed
- Auto-trim username field in login form.
- Auto-focus case/unit field when showing inventory count page.
## [0.1.20] - 2023-09-17
### Changed
- Validate amount when adding receiving quantity.
- Show "missing" quantities for item receiving.
## [0.1.19] - 2023-08-29
### Changed
- Add support for "missing" credit in mobile receiving.
## [0.1.18] - 2023-08-08
### Changed
- Add UPC quick lookup for View Product page.
- Add slot for "default panels" in View Product.
## [0.1.17] - 2023-08-03
### Changed
- Update to use lts/gallium version for nodejs.
## [0.1.16] - 2023-01-30
### Changed
- Add Prices panel for default product view.
## [0.1.15] - 2023-01-07
### Changed
- Add basic components for Product CRUD.
- Update dependencies.
- Center logo and text for home page.
## [0.1.14] - 2022-11-16 ## [0.1.14] - 2022-11-16
### Changed ### Changed
- Detect 404 notfound when viewing CRUD record; warn user accordingly. - Detect 404 notfound when viewing CRUD record; warn user accordingly.

14160
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "byjove", "name": "byjove",
"version": "0.1.26", "version": "0.1.14",
"description": "Generic-ish app components for Vue.js frontend to Tailbone API backend", "description": "Generic-ish app components for Vue.js frontend to Tailbone API backend",
"keywords": [ "keywords": [
"rattail", "rattail",
@ -18,18 +18,18 @@
"build-watch": "vue-cli-service build --target lib --name byjove --watch src/index.js" "build-watch": "vue-cli-service build --target lib --name byjove --watch src/index.js"
}, },
"dependencies": { "dependencies": {
"vue": "^2.7.14" "vue": "^2.6.10"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-service": "^3.0.5", "@vue/cli-service": "^3.0.5",
"rollup": "^1.32.1", "rollup": "^1.26.3",
"rollup-plugin-buble": "^0.19.8", "rollup-plugin-buble": "^0.19.8",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-vue": "^5.1.9", "rollup-plugin-vue": "^5.1.2",
"vue-resource": "^1.5.3", "vue-resource": "^1.5.1",
"vue-router": "^3.6.5", "vue-router": "^3.1.3",
"vue-template-compiler": "^2.7.14", "vue-template-compiler": "^2.6.10",
"vuex": "^3.6.2" "vuex": "^3.1.1"
}, },
"main": "./dist/byjove.umd.js", "main": "./dist/byjove.umd.js",
"files": [ "files": [

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="has-text-centered"> <div>
<img :alt="`${appsettings.systemTitle} logo`" :src="appsettings.logo" /> <img :alt="`${appsettings.systemTitle} logo`" :src="appsettings.logo" />
<h1>Welcome to {{ appsettings.appTitle }}</h1> <h1>Welcome to {{ appsettings.appTitle }}</h1>
</div> </div>

View file

@ -7,8 +7,6 @@ import ByjoveLogin from './login'
import ByjoveAutocomplete from './autocomplete' import ByjoveAutocomplete from './autocomplete'
import ByjoveModelIndex from './model-index' import ByjoveModelIndex from './model-index'
import ByjoveModelCrud from './model-crud' import ByjoveModelCrud from './model-crud'
import ByjoveScannerInput from './scanner-input'
import {ByjoveProducts, ByjoveProduct} from './products'
import ByjoveCustomerField from './customers' import ByjoveCustomerField from './customers'
import ByjoveInventory from './inventory' import ByjoveInventory from './inventory'
import ByjoveReceiving from './receiving' import ByjoveReceiving from './receiving'
@ -23,9 +21,6 @@ export {
ByjoveAutocomplete, ByjoveAutocomplete,
ByjoveModelIndex, ByjoveModelIndex,
ByjoveModelCrud, ByjoveModelCrud,
ByjoveScannerInput,
ByjoveProducts,
ByjoveProduct,
ByjoveCustomerField, ByjoveCustomerField,
ByjoveInventory, ByjoveInventory,
ByjoveReceiving, ByjoveReceiving,

View file

@ -44,15 +44,13 @@
<b-input v-model="row.cases" <b-input v-model="row.cases"
type="number" type="number"
step="any" step="any"
ref="cases"
:disabled="!shouldAllowCases()"> :disabled="!shouldAllowCases()">
</b-input> </b-input>
</b-field> </b-field>
<b-field label="Units" expanded> <b-field label="Units" expanded>
<b-input v-model="row.units" <b-input v-model="row.units"
type="number" type="number"
step="any" step="any">
ref="units">
</b-input> </b-input>
</b-field> </b-field>
</b-field> </b-field>
@ -83,10 +81,6 @@ export default {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
focusCases: {
type: Boolean,
default: false,
},
allowEdit: { allowEdit: {
type: Boolean, type: Boolean,
default: false, default: false,
@ -96,7 +90,6 @@ export default {
default: false, default: false,
}, },
}, },
data() { data() {
return { return {
row: {}, row: {},
@ -160,14 +153,6 @@ export default {
if (this.row.batch_executed || this.row.batch_complete) { if (this.row.batch_executed || this.row.batch_complete) {
// cannot edit this row, so view it instead // cannot edit this row, so view it instead
this.$router.push(this.getViewUrl()) this.$router.push(this.getViewUrl())
} else {
this.$nextTick(() => {
if (this.shouldAllowCases() && this.focusCases) {
this.$refs.cases.focus()
} else {
this.$refs.units.focus()
}
})
} }
}, response => { }, response => {
if (response.status == 403) { // forbidden; redirect to model index if (response.status == 403) { // forbidden; redirect to model index

View file

@ -6,7 +6,7 @@
</byjove-logo> </byjove-logo>
<b-field label="Username"> <b-field label="Username">
<b-input v-model.trim="username" /> <b-input v-model="username" />
</b-field> </b-field>
<b-field label="Password"> <b-field label="Password">

View file

@ -189,10 +189,10 @@ export default {
rowFilters: { rowFilters: {
type: Function, type: Function,
default: (uuid) => { default: (uuid) => {
return [ return JSON.stringify([
{field: 'batch_uuid', op: 'eq', value: uuid}, {field: 'batch_uuid', op: 'eq', value: uuid},
{field: 'removed', op: 'eq', value: false}, {field: 'removed', op: 'eq', value: false},
] ])
}, },
}, },
rowOrderBy: { rowOrderBy: {
@ -321,11 +321,6 @@ export default {
methods: { methods: {
clear() {
this.record = {}
this.$emit('refresh', this.record)
},
getModelSlug() { getModelSlug() {
if (this.modelSlug) { if (this.modelSlug) {
return this.modelSlug return this.modelSlug
@ -511,14 +506,7 @@ export default {
this.record = response.data.data this.record = response.data.data
this.$emit('refresh', this.record) this.$emit('refresh', this.record)
if (this.hasRows) { if (this.hasRows) {
// TODO: was seeing occasional errors when a batch
// view was loaded, and it tried to fetch rows but
// somehow the uuid was not passed along and
// server failed to build a query filter. so
// hoping the nextTick() delay fixes it..?
this.$nextTick(() => {
this.fetchRows(uuid) this.fetchRows(uuid)
})
} }
}, response => { }, response => {
if (response.status == 403) { // forbidden if (response.status == 403) { // forbidden
@ -545,7 +533,7 @@ export default {
fetchRows(uuid) { fetchRows(uuid) {
let params = { let params = {
filters: JSON.stringify(this.rowFilters(uuid)), filters: this.rowFilters(uuid),
orderBy: this.rowOrderBy, orderBy: this.rowOrderBy,
ascending: this.rowOrderAscending ? 1 : 0, ascending: this.rowOrderAscending ? 1 : 0,
} }

View file

@ -62,10 +62,6 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
wantInitialResults: {
type: Boolean,
default: true,
},
allowCreate: { allowCreate: {
type: Boolean, type: Boolean,
default: true, default: true,
@ -87,9 +83,7 @@ export default {
} }
}, },
mounted() { mounted() {
if (this.wantInitialResults) {
this.fetchData() this.fetchData()
}
}, },
watch: { watch: {
'apiIndexFilters' (to, from) { 'apiIndexFilters' (to, from) {

View file

@ -1,152 +0,0 @@
<template>
<b-input :name="name"
:value="value"
ref="input"
:placeholder="placeholder"
:size="size"
:icon-pack="iconPack"
:icon="icon"
:disabled="disabled"
:custom-class="customClass"
@focus="notifyFocus"
@blur="notifyBlur"
@keydown.native="keyDown"
@input="valueChanged"
/>
</template>
<script>
export default {
name: 'NumericInput',
props: {
name: String,
value: [Number, String],
placeholder: String,
iconPack: String,
icon: String,
size: String,
disabled: Boolean,
allowEnter: Boolean,
customClass: String,
},
methods: {
focus() {
this.$refs.input.focus()
},
notifyFocus(event) {
this.$emit('focus', event)
},
notifyBlur(event) {
this.$emit('blur', event)
},
keyDown(event) {
// by default we only allow numeric keys, and general navigation
// keys, but we might also allow Enter key
if (!this.key_modifies(event) && !this.key_allowed(event)) {
if (!this.allowEnter || event.which != 13) {
event.preventDefault()
}
}
},
/*
* Determine if a keypress would modify the value of a textbox.
*
* Note that this implies that the keypress is also *valid* in the context of a
* numeric textbox.
*
* Returns `true` if the keypress is valid and would modify the textbox value,
* or `false` otherwise.
*/
key_modifies(event) {
if (event.which >= 48 && event.which <= 57) { // Numeric (QWERTY)
if (! event.shiftKey) { // shift key means punctuation instead of numeric
return true;
}
} else if (event.which >= 96 && event.which <= 105) { // Numeric (10-Key)
return true;
} else if (event.which == 109 || event.which == 173) { // hyphen (negative sign)
return true;
} else if (event.which == 110 || event.which == 190) { // period/decimal
return true;
} else if (event.which == 8) { // Backspace
return true;
} else if (event.which == 46) { // Delete
return true;
} else if (event.ctrlKey && event.which == 86) { // Ctrl+V
return true;
} else if (event.ctrlKey && event.which == 88) { // Ctrl+X
return true;
}
return false;
},
/*
* Determine if a keypress is allowed in the context of a textbox.
*
* The purpose of this function is to let certain "special" keys (e.g. function
* and navigational keys) to pass through, so they may be processed as they
* would for a normal textbox.
*
* Note that this function does *not* check for keys which would actually
* modify the value of the textbox. It is assumed that the caller will have
* already used `key_modifies()` for that.
*
* Returns `true` if the keypress is allowed, or `false` otherwise.
*/
key_allowed(event) {
// Allow anything with modifiers (except Shift).
if (event.altKey || event.ctrlKey || event.metaKey) {
// ...but don't allow Ctrl+X or Ctrl+V
return event.which != 86 && event.which != 88;
}
// Allow function keys.
if (event.which >= 112 && event.which <= 123) {
return true;
}
// Allow Home/End/arrow keys.
if (event.which >= 35 && event.which <= 40) {
return true;
}
// Allow Tab key.
if (event.which == 9) {
return true;
}
// allow Escape key
if (event.which == 27) {
return true;
}
return false;
},
select() {
this.$el.children[0].select()
},
valueChanged(value) {
this.$emit('input', value)
}
},
}
</script>

View file

@ -1,28 +0,0 @@
// Import vue component
import NumericInput from './NumericInput.vue'
// Declare install function executed by Vue.use()
export function install(Vue) {
if (install.installed) return;
install.installed = true;
Vue.component('NumericInput', NumericInput);
}
// Create module definition for Vue.use()
const plugin = {
install,
};
// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}
// To allow use as module (npm/webpack/etc.) export component
export default NumericInput

View file

@ -1,200 +0,0 @@
<template>
<div>
<byjove-model-crud ref="modelCrud"
model-name="Product"
:label-renderer="renderLabel"
:allow-edit="allowEdit"
:mode="mode"
@refresh="record => { product = record }"
v-show="!scannerSubmitting"
class="block">
<div style="display: flex; justify-content: space-between; margin-bottom: 0.5rem;">
<div style="display: flex; flex-direction: column;">
<b-field label="Brand">
{{ product.brand_name }}
</b-field>
<b-field label="Description">
{{ product.description }}
</b-field>
<b-field label="Size">
{{ product.size }}
</b-field>
</div>
<img :src="product.image_url" />
</div>
<slot name="default-panels" :product="product">
<b-collapse class="card"
animation="slide"
:open="false"
aria-id="contentIdForVendors">
<template #trigger="props">
<div class="card-header"
role="button"
aria-controls="contentIdForVendors"
:aria-expanded="props.open">
<p class="card-header-title">
Vendors
</p>
<a class="card-header-icon">
<b-icon pack="fas"
:icon="props.open ? 'angle-up' : 'angle-down'">
</b-icon>
</a>
</div>
</template>
<div class="card-content">
<div class="content">
<b-field label="Preferred">
{{ product.vendor_name }} @ {{ product.default_unit_cost_display }}
</b-field>
<b-table :data="product.costs">
<b-table-column label="Vendor"
field="vendor_name"
v-slot="props">
{{ props.row.vendor_name }}
</b-table-column>
<b-table-column label="Unit Cost"
field="unit_cost"
v-slot="props">
{{ props.row.unit_cost }}
</b-table-column>
</b-table>
</div>
</div>
</b-collapse>
<b-collapse class="card"
animation="slide"
:open="false"
aria-id="contentIdForPrices">
<template #trigger="props">
<div class="card-header"
role="button"
aria-controls="contentIdForPrices"
:aria-expanded="props.open">
<p class="card-header-title">
Prices
</p>
<a class="card-header-icon">
<b-icon pack="fas"
:icon="props.open ? 'angle-up' : 'angle-down'">
</b-icon>
</a>
</div>
</template>
<div class="card-content">
<div class="content">
<b-field label="Reg. Price">
{{ product.unit_price_display }}
</b-field>
<b-field label="Sale Price"
v-if="product.sale_price">
{{ product.sale_price_display }}
</b-field>
<b-field label="Sale Ends"
v-if="product.sale_price">
{{ product.sale_ends_display }}
</b-field>
<b-field label="TPR Price"
v-if="product.tpr_price">
{{ product.tpr_price_display }}
</b-field>
<b-field label="TPR Ends"
v-if="product.tpr_price">
{{ product.tpr_ends_display }}
</b-field>
</div>
</div>
</b-collapse>
</slot>
<slot name="extra-panels" :product="product"></slot>
</byjove-model-crud>
<byjove-scanner-input ref="scannerInput"
class="block"
@submit="scannerSubmit">
</byjove-scanner-input>
</div>
</template>
<script>
import ByjoveModelCrud from '../model-crud'
import ByjoveScannerInput from '../scanner-input'
export default {
name: 'Product',
props: {
mode: String,
allowEdit: {
type: Boolean,
default: false,
},
},
components: {
ByjoveModelCrud,
ByjoveScannerInput,
},
data: function() {
return {
product: {},
scannerSubmitting: false,
}
},
methods: {
renderLabel(product) {
return product.product_key
},
scannerSubmit(entry) {
this.scannerSubmitting = true
let url = '/api/products/quick-lookup'
let params = {entry: entry}
this.$http.get(url, {params: params}).then(response => {
if (response.data.error) {
this.$buefy.toast.open({
message: response.data.error,
type: 'is-danger',
})
} else {
this.$refs.scannerInput.clear()
this.$refs.modelCrud.clear()
this.$router.push(`/products/${response.data.product.uuid}`)
}
this.scannerSubmitting = false
}, response => {
this.$buefy.toast.open({
message: "Unknown error!",
type: 'is-danger',
})
this.scannerSubmitting = false
})
},
},
}
</script>

View file

@ -1,55 +0,0 @@
<template>
<byjove-model-index model-name="Product"
:want-initial-results="wantInitialResults"
:allow-create="allowCreate">
<byjove-scanner-input @submit="scannerSubmit">
</byjove-scanner-input>
</byjove-model-index>
</template>
<script>
import ByjoveModelIndex from '../model-index'
import ByjoveScannerInput from '../scanner-input'
export default {
name: 'ByjoveProducts',
components: {
ByjoveModelIndex,
ByjoveScannerInput,
},
props: {
wantInitialResults: {
type: Boolean,
default: false,
},
allowCreate: {
type: Boolean,
default: false,
},
},
methods: {
scannerSubmit(entry) {
let url = '/api/products/quick-lookup'
let params = {entry: entry}
this.$http.get(url, {params: params}).then(response => {
if (response.data.error) {
this.$buefy.toast.open({
message: response.data.error,
type: 'is-danger',
})
} else {
this.$router.push(`/products/${response.data.product.uuid}`)
}
}, response => {
this.$buefy.toast.open({
message: "Unknown error!",
type: 'is-danger',
})
})
},
},
}
</script>

View file

@ -1,29 +0,0 @@
import ByjoveProducts from './ByjoveProducts'
import ByjoveProduct from './ByjoveProduct'
// Declare install function executed by Vue.use()
export function install(Vue) {
if (install.installed) return;
install.installed = true;
Vue.component('ByjoveProducts', ByjoveProducts);
Vue.component('ByjoveProduct', ByjoveProduct);
}
// Create module definition for Vue.use()
const plugin = {
install,
};
// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}
export {ByjoveProducts}
export {ByjoveProduct}

View file

@ -25,16 +25,58 @@
{{ row.description }} {{ row.size }} {{ row.description }} {{ row.size }}
</p> </p>
<p v-if="row.invoice_number"
class="has-text-weight-bold">
Invoice # {{ row.invoice_number }}
</p>
<p v-if="allowCases" <p v-if="allowCases"
class="has-text-weight-bold"> class="has-text-weight-bold">
1 CS = {{ row.case_quantity || 1 }} {{ row.unit_uom }} 1 CS = {{ row.case_quantity || 1 }} {{ row.unit_uom }}
</p> </p>
<table class="receiving-quantities">
<tr v-if="row.order_quantities_known">
<td>ordered</td>
<td>
<span v-if="allowCases">
{{ row.cases_ordered || 0}} /
</span>
{{ row.units_ordered || 0}}
</td>
</tr>
<tr v-if="row.order_quantities_known">
<td>shipped</td>
<td>
<span v-if="allowCases">
{{ row.cases_shipped || 0}} /
</span>
{{ row.units_shipped || 0}}
</td>
</tr>
<tr>
<td>received</td>
<td>
<span v-if="allowCases">
{{ row.cases_received || 0}} /
</span>
{{ row.units_received || 0}}
</td>
</tr>
<tr>
<td>damaged</td>
<td>
<span v-if="allowCases">
{{ row.cases_damaged || 0}} /
</span>
{{ row.units_damaged || 0}}
</td>
</tr>
<tr v-if="allowExpired">
<td>expired</td>
<td>
<span v-if="allowCases">
{{ row.cases_expired || 0}} /
</span>
{{ row.units_expired || 0}}
</td>
</tr>
</table>
</div> </div>
<div> <div>
@ -42,56 +84,6 @@
</div> </div>
</div> </div>
<div class="columns is-mobile">
<div class="column">
<div v-if="row.order_quantities_known"
style="display: flex; gap: 0.3rem;">
<span style="flex-grow: 1;">ordered</span>
<span v-if="allowCases">{{ row.cases_ordered || 0 }} /</span>
<span>{{ row.units_ordered || 0}}</span>
</div>
<div v-if="row.order_quantities_known"
style="display: flex; gap: 0.3rem;">
<span style="flex-grow: 1;">shipped</span>
<span v-if="allowCases">{{ row.cases_shipped || 0 }} /</span>
<span>{{ row.units_shipped || 0}}</span>
</div>
<div style="display: flex; gap: 0.3rem;">
<span style="flex-grow: 1;">received</span>
<span v-if="allowCases">{{ row.cases_received || 0 }} /</span>
<span>{{ row.units_received || 0}}</span>
</div>
</div>
<div class="column">
<div style="display: flex; gap: 0.3rem;">
<span style="flex-grow: 1;">damaged</span>
<span v-if="allowCases">{{ row.cases_damaged || 0 }} /</span>
<span>{{ row.units_damaged || 0}}</span>
</div>
<div v-if="allowExpired"
style="display: flex; gap: 0.3rem;">
<span style="flex-grow: 1;">expired</span>
<span v-if="allowCases">{{ row.cases_expired || 0 }} /</span>
<span>{{ row.units_expired || 0}}</span>
</div>
<div style="display: flex; gap: 0.3rem;">
<span style="flex-grow: 1;">missing</span>
<span v-if="allowCases">{{ row.cases_missing || 0 }} /</span>
<span>{{ row.units_missing || 0}}</span>
</div>
</div>
</div>
<div v-if="row.received_alert" <div v-if="row.received_alert"
class="has-text-danger"> class="has-text-danger">
<br /> <br />
@ -118,36 +110,28 @@
expanded> expanded>
{{ row.quick_receive_text }} {{ row.quick_receive_text }}
</b-button> </b-button>
<div v-if="!row.quick_receive_all" <div v-if="!row.quick_receive_all">
style="margin: auto;"> <div class="buttons"
<b-button v-if="allowCases" v-if="allowCases">
type="is-primary" <b-button type="is-primary"
@click="addQuickAmount(1, 'CS')" @click="addQuickAmount(1, 'CS')"
expanded expanded>
style="margin-bottom: 1rem;">
Receive 1 CS Receive 1 CS
</b-button> </b-button>
<div> </div>
<div class="buttons has-text-centered" style="width: 100%;">
<b-button type="is-primary" <b-button type="is-primary"
size="is-small"
@click="addQuickAmount(1, row.unit_uom)"> @click="addQuickAmount(1, row.unit_uom)">
1 {{ row.unit_uom }} 1 {{ row.unit_uom }}
</b-button> </b-button>
<b-button type="is-primary" <b-button type="is-primary"
size="is-small"
@click="addQuickAmount(3, row.unit_uom)"> @click="addQuickAmount(3, row.unit_uom)">
3 {{ row.unit_uom }} 3 {{ row.unit_uom }}
</b-button> </b-button>
<b-button type="is-primary" <b-button type="is-primary"
size="is-small"
@click="addQuickAmount(6, row.unit_uom)"> @click="addQuickAmount(6, row.unit_uom)">
6 {{ row.unit_uom }} 6 {{ row.unit_uom }}
</b-button> </b-button>
<b-button type="is-primary"
size="is-small"
@click="addQuickAmount(12, row.unit_uom)">
12 {{ row.unit_uom }}
</b-button>
</div> </div>
</div> </div>
</div> </div>
@ -159,13 +143,10 @@
<b-field grouped> <b-field grouped>
<b-field class="control"> <b-field class="control">
<numeric-input v-if="allowDecimalQuantities" <b-input v-model="inputQuantity"
v-model="inputQuantity"
custom-class="receiving-quantity-input" />
<b-input v-else
v-model="inputQuantity"
type="number" type="number"
custom-class="receiving-quantity-input" /> custom-class="receiving-quantity-input">
</b-input>
</b-field> </b-field>
<b-field class="control"> <b-field class="control">
<b-radio-button v-model="inputUOM" <b-radio-button v-model="inputUOM"
@ -195,15 +176,10 @@
<span>EXPD</span> <span>EXPD</span>
</b-radio-button> </b-radio-button>
<b-radio-button v-model="inputType" <b-radio-button v-model="inputType"
native-value="missing"> native-value="mispick"
<span>DNR</span> disabled>
<span>MSPK</span>
</b-radio-button> </b-radio-button>
<!-- TODO: maybe some day... -->
<!-- <b-radio-button v-model="inputType" -->
<!-- native-value="mispick" -->
<!-- disabled> -->
<!-- <span>MSPK</span> -->
<!-- </b-radio-button> -->
</b-field> </b-field>
<b-field v-if="inputType == 'expired'" <b-field v-if="inputType == 'expired'"
@ -214,7 +190,6 @@
</b-field> </b-field>
<div class="buttons"> <div class="buttons">
<div style="margin: auto;">
<b-button type="is-primary" <b-button type="is-primary"
@click="addAmount()" @click="addAmount()"
:disabled="addAmountDisabled"> :disabled="addAmountDisabled">
@ -222,10 +197,9 @@
</b-button> </b-button>
<b-button type="is-warning" <b-button type="is-warning"
@click="subtractAmount()"> @click="subtractAmount()">
Subtract Subtract Amount
</b-button> </b-button>
</div> </div>
</div>
<p class="has-text-centered is-italic">OR</p> <p class="has-text-centered is-italic">OR</p>
<br /> <br />
@ -249,11 +223,8 @@
</template> </template>
<script> <script>
import NumericInput from '../numeric-input/NumericInput.vue'
export default { export default {
name: 'ByjoveReceiving', name: 'ByjoveReceiving',
components: {NumericInput},
props: { props: {
productKey: { productKey: {
type: String, type: String,
@ -263,10 +234,6 @@ export default {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
allowDecimalQuantities: {
type: Boolean,
default: false,
},
allowExpired: { allowExpired: {
type: Boolean, type: Boolean,
default: true, default: true,
@ -342,17 +309,6 @@ export default {
}, },
addAmount() { addAmount() {
let amount = this.inputQuantity
amount = this.allowDecimalQuantities ? parseFloat(amount) : parseInt(amount)
if (!amount) {
this.$buefy.toast.open({
message: "Please specify an amount",
type: 'is-info',
})
return
}
let url = `/api/receiving-batch-row/${this.row.uuid}/receive` let url = `/api/receiving-batch-row/${this.row.uuid}/receive`
let params = { let params = {
row: this.row.uuid, row: this.row.uuid,
@ -364,23 +320,23 @@ export default {
} }
if (this.inputUOM == 'CS') { if (this.inputUOM == 'CS') {
params.cases = amount params.cases = this.inputQuantity
} else { } else {
params.units = amount params.units = this.inputQuantity
} }
this.$http.post(url, params).then(response => { this.$http.post(url, params).then(response => {
if (!response.data.error) { if (response.data.data) {
this.$router.push(`/receiving/${this.row.batch_uuid}`) this.$router.push(`/receiving/${this.row.batch_uuid}`)
} else { } else {
this.$buefy.toast.open({ this.$buefy.toast.open({
message: response.data.error, message: response.data.error || "Failed to post receiving!",
type: 'is-danger', type: 'is-danger',
}) })
} }
}, response => { }, response => {
this.$buefy.toast.open({ this.$buefy.toast.open({
message: "Save failed: unknown error", message: "Failed to post receiving!",
type: 'is-danger', type: 'is-danger',
}) })
}) })
@ -404,6 +360,6 @@ export default {
padding: 0px 10px 0px 0px; padding: 0px 10px 0px 0px;
} }
.input.receiving-quantity-input { .input.receiving-quantity-input {
width: 6rem; width: 10rem;
} }
</style> </style>

View file

@ -1,60 +0,0 @@
<template>
<div class="scanner-input">
<b-input v-model="quickEntry"
placeholder="Scan/Enter Code"
icon="search"
@keypress.native="quickKey">
</b-input>
</div>
</template>
<script>
export default {
name: 'ByjoveScannerInput',
data() {
return {
quickEntry: '',
}
},
mounted() {
window.addEventListener('keypress', this.globalKey)
},
beforeDestroy() {
window.removeEventListener('keypress', this.globalKey)
},
methods: {
clear() {
this.quickEntry = ''
},
globalKey(event) {
if (event.target.tagName == 'BODY') {
// mimic keyboard wedge
if (event.charCode >= 48 && event.charCode <= 57) { // numeric (qwerty)
this.$nextTick(() => {
this.quickEntry += event.key
})
} else if (event.keyCode == 13) { // enter
this.submitQuickEntry()
}
}
},
quickKey(event) {
if (event.keyCode == 13) { // enter
this.submitQuickEntry()
}
},
submitQuickEntry() {
if (this.quickEntry) {
this.$emit('submit', this.quickEntry)
}
},
},
}
</script>

View file

@ -1,28 +0,0 @@
// Import vue component
import ByjoveScannerInput from './ByjoveScannerInput.vue'
// Declare install function executed by Vue.use()
export function install(Vue) {
if (install.installed) return;
install.installed = true;
Vue.component('ByjoveScannerInput', ByjoveScannerInput);
}
// Create module definition for Vue.use()
const plugin = {
install,
};
// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}
// To allow use as module (npm/webpack/etc.) export component
export default ByjoveScannerInput

View file

@ -13,5 +13,4 @@ def release(c):
""" """
c.run("find . -name '*~' -delete") c.run("find . -name '*~' -delete")
c.run('npm run build') c.run('npm run build')
otp = c.run('otp npm').stdout.strip() c.run('npm publish')
c.run('npm publish --otp {}'.format(otp))