Add basic "receiving" support for theo mobile
This commit is contained in:
		
							parent
							
								
									3779d7b9f4
								
							
						
					
					
						commit
						22aad5e31f
					
				
					 8 changed files with 917 additions and 0 deletions
				
			
		|  | @ -11,6 +11,12 @@ | ||||||
|       <router-link to="/ordering/">Ordering</router-link> |       <router-link to="/ordering/">Ordering</router-link> | ||||||
|     </b-dropdown-item> |     </b-dropdown-item> | ||||||
| 
 | 
 | ||||||
|  |     <b-dropdown-item v-if="$hasPerm('receiving.list')" | ||||||
|  |                      aria-role="menuitem" | ||||||
|  |                      has-link> | ||||||
|  |       <router-link to="/receiving/">Receiving</router-link> | ||||||
|  |     </b-dropdown-item> | ||||||
|  | 
 | ||||||
|   </byjove-menu> |   </byjove-menu> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import VueRouter from 'vue-router' | ||||||
| import Home from '../views/Home.vue' | import Home from '../views/Home.vue' | ||||||
| import Login from '../views/Login.vue' | import Login from '../views/Login.vue' | ||||||
| import {OrderingBatches, OrderingBatch, OrderingBatchRow, OrderingBatchWorksheet} from '../views/ordering' | import {OrderingBatches, OrderingBatch, OrderingBatchRow, OrderingBatchWorksheet} from '../views/ordering' | ||||||
|  | import {ReceivingBatches, ReceivingBatch, ReceivingBatchRow, ReceivingBatchRowReceive} from '../views/receiving' | ||||||
| 
 | 
 | ||||||
| Vue.use(VueRouter) | Vue.use(VueRouter) | ||||||
| 
 | 
 | ||||||
|  | @ -75,6 +76,44 @@ const routes = [ | ||||||
|         name: 'ordering.worksheet', |         name: 'ordering.worksheet', | ||||||
|         component: OrderingBatchWorksheet, |         component: OrderingBatchWorksheet, | ||||||
|     }, |     }, | ||||||
|  | 
 | ||||||
|  |     //////////////////////////////
 | ||||||
|  |     // Receiving
 | ||||||
|  |     //////////////////////////////
 | ||||||
|  |     { | ||||||
|  |         path: '/receiving/', | ||||||
|  |         name: 'receiving', | ||||||
|  |         component: ReceivingBatches, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         path: '/receiving/new', | ||||||
|  |         name: 'receiving.new', | ||||||
|  |         component: ReceivingBatch, | ||||||
|  |         props: {mode: 'creating'}, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         path: '/receiving/:uuid', | ||||||
|  |         name: 'receiving.view', | ||||||
|  |         component: ReceivingBatch, | ||||||
|  |         props: {mode: 'viewing'}, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         path: '/receiving/:uuid/edit', | ||||||
|  |         name: 'receiving.edit', | ||||||
|  |         component: ReceivingBatch, | ||||||
|  |         props: {mode: 'editing'}, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         path: '/receiving/rows/:uuid', | ||||||
|  |         name: 'receiving.rows.view', | ||||||
|  |         component: ReceivingBatchRow, | ||||||
|  |         props: {mode: 'viewing'}, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         path: '/receiving/rows/:uuid/receive', | ||||||
|  |         name: 'receiving.rows.receive', | ||||||
|  |         component: ReceivingBatchRowReceive, | ||||||
|  |     }, | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| const router = new VueRouter({ | const router = new VueRouter({ | ||||||
|  |  | ||||||
							
								
								
									
										608
									
								
								mobile/src/views/receiving/ReceivingBatch.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										608
									
								
								mobile/src/views/receiving/ReceivingBatch.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,608 @@ | ||||||
|  | <template> | ||||||
|  |   <byjove-model-crud model-name="PurchaseBatch" | ||||||
|  |                      model-index-title="Receiving" | ||||||
|  |                      model-title="Receiving Batch" | ||||||
|  |                      model-title-plural="Receiving Batches" | ||||||
|  |                      model-path-prefix="/receiving" | ||||||
|  |                      model-permission-prefix="receiving" | ||||||
|  |                      row-path-prefix="/receiving/rows" | ||||||
|  |                      :mode="mode" | ||||||
|  |                      @refresh="refreshData" | ||||||
|  |                      api-index-url="/api/receiving-batches" | ||||||
|  |                      api-object-url="/api/receiving-batch/" | ||||||
|  |                      :header-label-renderer="renderHeaderLabel" | ||||||
|  |                      :row-label-renderer="renderRowLabel" | ||||||
|  |                      :allow-edit="false" | ||||||
|  |                      has-rows | ||||||
|  |                      api-rows-url="/api/receiving-batch-rows" | ||||||
|  |                      @save="save" | ||||||
|  |                      :save-disabled="saveDisabled()" | ||||||
|  |                      :hide-buttons="hideButtons" | ||||||
|  |                      :row-filters="rowFilters" | ||||||
|  |                      :row-route-getter="getRowRoute" | ||||||
|  |                      ref="modelCrud" | ||||||
|  |                      > | ||||||
|  | 
 | ||||||
|  |     <div v-if="mode == 'creating'"> | ||||||
|  | 
 | ||||||
|  |       <b-field label="Vendor" | ||||||
|  |                :type="{'is-danger': !batch.vendor_uuid}"> | ||||||
|  |         <byjove-autocomplete v-model="batch.vendor_uuid" | ||||||
|  |                              service-url="/api/vendors/autocomplete"> | ||||||
|  |         </byjove-autocomplete> | ||||||
|  |       </b-field> | ||||||
|  | 
 | ||||||
|  |       <div v-show="batch.vendor_uuid"> | ||||||
|  | 
 | ||||||
|  |         <div v-show="!createMode" | ||||||
|  |              class="buttons"> | ||||||
|  | 
 | ||||||
|  |           <b-button type="is-primary" | ||||||
|  |                     @click="receiveFromPO()"> | ||||||
|  |             Receive from PO | ||||||
|  |           </b-button> | ||||||
|  | 
 | ||||||
|  |           <b-button type="is-primary" | ||||||
|  |                     @click="receiveFromScratch()"> | ||||||
|  |             Receive from Scratch | ||||||
|  |           </b-button> | ||||||
|  | 
 | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div v-show="createMode == 'from_po'"> | ||||||
|  |           <p>Please choose a Purchase Order to receive:</p> | ||||||
|  |           <div> | ||||||
|  | 
 | ||||||
|  |             <b-menu> | ||||||
|  |               <b-menu-list> | ||||||
|  |                 <b-menu-item v-for="po in purchaseOrders" | ||||||
|  |                              :key="po.uuid" | ||||||
|  |                              tag="a" href="#" | ||||||
|  |                              @click.prevent="receivePO(po)"> | ||||||
|  |                   <template slot="label" slot-scope="props"> | ||||||
|  |                     <span>{{ po.display }}</span> | ||||||
|  |                   </template> | ||||||
|  |                 </b-menu-item> | ||||||
|  |               </b-menu-list> | ||||||
|  |             </b-menu> | ||||||
|  | 
 | ||||||
|  |             <div v-if="!fetchingPurchaseOrders && !purchaseOrders.length" | ||||||
|  |                  class="has-text-info"> | ||||||
|  |               (no purchase orders found) | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <b-loading :active="fetchingPurchaseOrders || postingFormData"></b-loading> | ||||||
|  |           </div> | ||||||
|  | 
 | ||||||
|  |           <br /> | ||||||
|  |           <b-button type="is-primary" | ||||||
|  |                     @click="receiveFromScratch()"> | ||||||
|  |             Receive from Scratch | ||||||
|  |           </b-button> | ||||||
|  | 
 | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <!-- <b-field label="Description"> --> | ||||||
|  |         <!--   <b-input v-model="batch.description"> --> | ||||||
|  |         <!--   </b-input> --> | ||||||
|  |         <!-- </b-field> --> | ||||||
|  | 
 | ||||||
|  |         <!-- <b-field label="Notes"> --> | ||||||
|  |         <!--   <b-input v-model="batch.notes" --> | ||||||
|  |         <!--            type="textarea"> --> | ||||||
|  |         <!--   </b-input> --> | ||||||
|  |         <!-- </b-field> --> | ||||||
|  |       </div> | ||||||
|  |       <br /> | ||||||
|  |     </div> <!-- end creating --> | ||||||
|  | 
 | ||||||
|  |     <div v-if="mode == 'viewing'"> | ||||||
|  | 
 | ||||||
|  |       <b-field label="Vendor"> | ||||||
|  |         <span> | ||||||
|  |           {{ batch.vendor_display }} | ||||||
|  |         </span> | ||||||
|  |       </b-field> | ||||||
|  | 
 | ||||||
|  |       <b-field label="Department"> | ||||||
|  |         <span> | ||||||
|  |           {{ batch.department_display }} | ||||||
|  |         </span> | ||||||
|  |       </b-field> | ||||||
|  | 
 | ||||||
|  |       <!-- <div v-if="!batch.executed && unprintedSpecialOrders" --> | ||||||
|  |       <!--      class="buttons"> --> | ||||||
|  |       <!--   <b-button type="is-primary" --> | ||||||
|  |       <!--             @click="printSpecialOrderTickets()"> --> | ||||||
|  |       <!--     Print {{ batch.special_orders.length }} Special Order Tickets --> | ||||||
|  |       <!--   </b-button> --> | ||||||
|  |       <!-- </div> --> | ||||||
|  | 
 | ||||||
|  |       <div v-if="!batch.executed && batch.complete"> | ||||||
|  |         <b-field label="Description"> | ||||||
|  |           <span> | ||||||
|  |             {{ batch.description }} | ||||||
|  |           </span> | ||||||
|  |         </b-field> | ||||||
|  | 
 | ||||||
|  |         <b-field label="Notes" | ||||||
|  |                  v-show="batch.notes"> | ||||||
|  |           <span> | ||||||
|  |             {{ batch.notes }} | ||||||
|  |           </span> | ||||||
|  |         </b-field> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <br v-if="!batch.executed" /> | ||||||
|  | 
 | ||||||
|  |       <div v-if="batch.executed"> | ||||||
|  | 
 | ||||||
|  |         <b-field label="Created"> | ||||||
|  |           <span> | ||||||
|  |             {{ batch.created }} | ||||||
|  |           </span> | ||||||
|  |         </b-field> | ||||||
|  | 
 | ||||||
|  |         <b-field label="Created By"> | ||||||
|  |           <span> | ||||||
|  |             {{ batch.created_by_display }} | ||||||
|  |           </span> | ||||||
|  |         </b-field> | ||||||
|  | 
 | ||||||
|  |         <b-field label="Row Count"> | ||||||
|  |           <span> | ||||||
|  |             {{ batch.rowcount }} | ||||||
|  |           </span> | ||||||
|  |         </b-field> | ||||||
|  | 
 | ||||||
|  |         <b-field label="Executed"> | ||||||
|  |           <span> | ||||||
|  |             {{ batch.executed }} | ||||||
|  |           </span> | ||||||
|  |         </b-field> | ||||||
|  | 
 | ||||||
|  |         <b-field label="Executed By"> | ||||||
|  |           <span> | ||||||
|  |             {{ batch.executed_by_display }} | ||||||
|  |           </span> | ||||||
|  |         </b-field> | ||||||
|  | 
 | ||||||
|  |       </div> <!-- end executed --> | ||||||
|  | 
 | ||||||
|  |     </div> <!-- end viewing --> | ||||||
|  | 
 | ||||||
|  |     <!-- <b-field label="Vendor" --> | ||||||
|  |     <!--          :type="{'is-danger': mode == 'creating' && !batch.vendor_uuid}"> --> | ||||||
|  |     <!--   <byjove-autocomplete v-if="mode == 'creating'" --> | ||||||
|  |     <!--                        v-model="batch.vendor_uuid" --> | ||||||
|  |     <!--                        service-url="/api/vendors/autocomplete"> --> | ||||||
|  |     <!--   </byjove-autocomplete> --> | ||||||
|  |     <!--   <span v-if="mode != 'creating'"> --> | ||||||
|  |     <!--     {{ batch.vendor_display }} --> | ||||||
|  |     <!--   </span> --> | ||||||
|  |     <!-- </b-field> --> | ||||||
|  | 
 | ||||||
|  |     <!-- <b-field label="Description"> --> | ||||||
|  |     <!--   <b-input v-if="mode == 'creating'" --> | ||||||
|  |     <!--            v-model="batch.description"> --> | ||||||
|  |     <!--   </b-input> --> | ||||||
|  |     <!--   <span v-if="mode != 'creating'"> --> | ||||||
|  |     <!--     {{ batch.description }} --> | ||||||
|  |     <!--   </span> --> | ||||||
|  |     <!-- </b-field> --> | ||||||
|  | 
 | ||||||
|  |     <!-- <b-field label="Notes" --> | ||||||
|  |     <!--          > --> | ||||||
|  |     <!--          <\!-- v-show="mode == 'creating' || batch.notes" -\-> --> | ||||||
|  |     <!--   <b-input v-if="mode == 'creating'" --> | ||||||
|  |     <!--            v-model="batch.notes" --> | ||||||
|  |     <!--            type="textarea"> --> | ||||||
|  |     <!--   </b-input> --> | ||||||
|  |     <!--   <span v-if="mode != 'creating'"> --> | ||||||
|  |     <!--     {{ batch.notes }} --> | ||||||
|  |     <!--   </span> --> | ||||||
|  |     <!-- </b-field> --> | ||||||
|  | 
 | ||||||
|  |     <!-- <b-field label="Created" --> | ||||||
|  |     <!--          v-if="mode != 'creating' && batch.executed"> --> | ||||||
|  |     <!--   <span> --> | ||||||
|  |     <!--     {{ batch.created }} --> | ||||||
|  |     <!--   </span> --> | ||||||
|  |     <!-- </b-field> --> | ||||||
|  | 
 | ||||||
|  |     <!-- <b-field label="Created By" --> | ||||||
|  |     <!--          v-if="mode != 'creating' && batch.executed"> --> | ||||||
|  |     <!--   <span> --> | ||||||
|  |     <!--     {{ batch.created_by_display }} --> | ||||||
|  |     <!--   </span> --> | ||||||
|  |     <!-- </b-field> --> | ||||||
|  | 
 | ||||||
|  |     <!-- <b-field label="Row Count" --> | ||||||
|  |     <!--          v-if="mode != 'creating' && batch.executed"> --> | ||||||
|  |     <!--   <span> --> | ||||||
|  |     <!--     {{ batch.rowcount }} --> | ||||||
|  |     <!--   </span> --> | ||||||
|  |     <!-- </b-field> --> | ||||||
|  | 
 | ||||||
|  |     <!-- <b-field label="Executed" --> | ||||||
|  |     <!--          v-if="mode != 'creating' && batch.executed"> --> | ||||||
|  |     <!--   <span> --> | ||||||
|  |     <!--     {{ batch.executed }} --> | ||||||
|  |     <!--   </span> --> | ||||||
|  |     <!-- </b-field> --> | ||||||
|  | 
 | ||||||
|  |     <!-- <b-field label="Executed By" --> | ||||||
|  |     <!--          v-if="mode != 'creating' && batch.executed"> --> | ||||||
|  |     <!--   <span> --> | ||||||
|  |     <!--     {{ batch.executed_by_display }} --> | ||||||
|  |     <!--   </span> --> | ||||||
|  |     <!-- </b-field> --> | ||||||
|  | 
 | ||||||
|  |     <template slot="quick-entry"> | ||||||
|  |       <div v-if="mode == 'viewing' && !batch.executed && !batch.complete"> | ||||||
|  |         <b-input v-model="quickEntry" | ||||||
|  |                  placeholder="Enter UPC" | ||||||
|  |                  icon="search" | ||||||
|  |                  @keypress.native="quickKey"> | ||||||
|  |         </b-input> | ||||||
|  |         <br /> | ||||||
|  |       </div> | ||||||
|  |     </template> | ||||||
|  | 
 | ||||||
|  |     <template slot="row-filters"> | ||||||
|  |       <b-field grouped group-multiline> | ||||||
|  |         <b-radio-button v-model="selectedRowFilter" | ||||||
|  |                         native-value="is_incomplete"> | ||||||
|  |           <span>incomplete</span> | ||||||
|  |         </b-radio-button> | ||||||
|  |         <b-radio-button v-model="selectedRowFilter" | ||||||
|  |                         native-value="is_unexpected"> | ||||||
|  |           <span>unexpected</span> | ||||||
|  |         </b-radio-button> | ||||||
|  |         <b-radio-button v-model="selectedRowFilter" | ||||||
|  |                         native-value="is_damaged"> | ||||||
|  |           <span>damaged</span> | ||||||
|  |         </b-radio-button> | ||||||
|  |         <b-radio-button v-model="selectedRowFilter" | ||||||
|  |                         native-value="is_invalid"> | ||||||
|  |           <span>invalid</span> | ||||||
|  |         </b-radio-button> | ||||||
|  |         <b-radio-button v-model="selectedRowFilter" | ||||||
|  |                         native-value="all"> | ||||||
|  |           <span>all</span> | ||||||
|  |         </b-radio-button> | ||||||
|  |       </b-field> | ||||||
|  |     </template> | ||||||
|  | 
 | ||||||
|  |     <template slot="footer" | ||||||
|  |               v-if="mode == 'viewing' && !batch.executed"> | ||||||
|  |       <br /> | ||||||
|  |       <div class="buttons is-centered"> | ||||||
|  |         <b-button v-if="!batch.complete" | ||||||
|  |                   type="is-primary" | ||||||
|  |                   @click="markReceivingComplete()"> | ||||||
|  |           Receiving is Complete! | ||||||
|  |         </b-button> | ||||||
|  |         <!-- <b-button v-if="batch.special_orders && (ticketsPrinted || !unprintedSpecialOrders)" --> | ||||||
|  |         <!--           type="is-primary" --> | ||||||
|  |         <!--           @click="printSpecialOrderTickets()"> --> | ||||||
|  |         <!--   Re-print {{ batch.special_orders.length }} Special Order Tickets --> | ||||||
|  |         <!-- </b-button> --> | ||||||
|  |       </div> | ||||||
|  |     </template> | ||||||
|  |   </byjove-model-crud> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import {ByjoveModelCrud, ByjoveAutocomplete} from 'byjove' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |     name: 'ReceivingBatch', | ||||||
|  |     props: { | ||||||
|  |         mode: String, | ||||||
|  |     }, | ||||||
|  |     components: { | ||||||
|  |         ByjoveModelCrud, | ||||||
|  |         ByjoveAutocomplete, | ||||||
|  |     }, | ||||||
|  |     data() { | ||||||
|  |         return { | ||||||
|  |             createMode: null, | ||||||
|  |             purchaseOrders: [], | ||||||
|  |             fetchingPurchaseOrders: false, | ||||||
|  |             postingFormData: false, | ||||||
|  |             batch: {}, | ||||||
|  |             quickEntry: '', | ||||||
|  |             ticketsPrinted: false, | ||||||
|  | 
 | ||||||
|  |             selectedRowFilter: 'is_incomplete', | ||||||
|  |             possibleRowFilters: { | ||||||
|  |                 'is_incomplete': [ | ||||||
|  |                     {field: 'is_incomplete', op: 'eq', value: true}, | ||||||
|  |                 ], | ||||||
|  |                 'is_unexpected': [ | ||||||
|  |                     {field: 'is_unexpected', op: 'eq', value: true}, | ||||||
|  |                 ], | ||||||
|  |                 'is_damaged': [ | ||||||
|  |                     {field: 'is_damaged', op: 'eq', value: true}, | ||||||
|  |                 ], | ||||||
|  |                 'is_invalid': [ | ||||||
|  |                     {field: 'is_invalid', op: 'eq', value: true}, | ||||||
|  |                 ], | ||||||
|  |                 'all': [], | ||||||
|  |             }, | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     computed: { | ||||||
|  |         hideButtons: function() { | ||||||
|  |             if (this.mode == 'creating') { | ||||||
|  |                 return true | ||||||
|  |             } | ||||||
|  |             return false | ||||||
|  |         }, | ||||||
|  |         // unprintedSpecialOrders() { | ||||||
|  |         //     if (!this.batch.special_orders) { | ||||||
|  |         //         return false | ||||||
|  |         //     } | ||||||
|  |         //     if (!this.batch.special_orders.length) { | ||||||
|  |         //         return false | ||||||
|  |         //     } | ||||||
|  |         //     if (!this.batch.params) { | ||||||
|  |         //         return true | ||||||
|  |         //     } | ||||||
|  |         //     if (this.batch.params.special_order_tickets_printed) { | ||||||
|  |         //         return false | ||||||
|  |         //     } | ||||||
|  |         //     return true | ||||||
|  |         // }, | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     watch: { | ||||||
|  | 
 | ||||||
|  |         'batch.vendor_uuid' (to, from) { | ||||||
|  |             // when vendor selection is cleared, we must also "forget" our | ||||||
|  |             // batch creation mode, so UI behaves as expected | ||||||
|  |             if (!to) { | ||||||
|  |                 this.createMode = null | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         'selectedRowFilter' (to, from) { | ||||||
|  |             // when user changes row filter, tell model-crud to fetch rows | ||||||
|  |             this.$refs.modelCrud.fetchRows(this.batch.uuid) | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     mounted() { | ||||||
|  |         window.addEventListener('keypress', this.globalKey) | ||||||
|  |     }, | ||||||
|  |     beforeDestroy() { | ||||||
|  |         window.removeEventListener('keypress', this.globalKey) | ||||||
|  |     }, | ||||||
|  |     methods: { | ||||||
|  | 
 | ||||||
|  |         renderHeaderLabel(batch) { | ||||||
|  |             let label = batch.id_str | ||||||
|  |             if (batch.executed) { | ||||||
|  |                 label += " (executed)" | ||||||
|  |             } else if (batch.complete) { | ||||||
|  |                 label += " (complete)" | ||||||
|  |             } else { | ||||||
|  |                 label += " (pending)" | ||||||
|  |             } | ||||||
|  |             return label | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         renderRowLabel(row) { | ||||||
|  |             return `(${row.cases_received || 0} / ${row.units_received || 0}) ${row.full_description}` | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         globalKey(event) { | ||||||
|  |             if (event.target.tagName == 'BODY') { | ||||||
|  | 
 | ||||||
|  |                 // mimic keyboard wedge | ||||||
|  |                 if (event.charCode >= 48 && event.charCode <= 57) { // numeric (qwerty) | ||||||
|  |                     this.$nextTick(function() { | ||||||
|  |                         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) { | ||||||
|  |                 return | ||||||
|  |             } | ||||||
|  |             let params = { | ||||||
|  |                 batch_uuid: this.batch.uuid, | ||||||
|  |                 quick_entry: this.quickEntry, | ||||||
|  |             } | ||||||
|  |             let url = '/api/receiving-batch-rows/quick-entry' | ||||||
|  |             this.$http.post(url, params).then(response => { | ||||||
|  |                 if (response.data.data) { | ||||||
|  |                     this.$router.push(`/receiving/rows/${response.data.data.uuid}/receive`) | ||||||
|  |                 } else { | ||||||
|  |                     this.$buefy.toast.open({ | ||||||
|  |                         message: response.data.error || "Failed to post quick entry!", | ||||||
|  |                         type: 'is-danger', | ||||||
|  |                         position: 'is-bottom', | ||||||
|  |                     }) | ||||||
|  |                 } | ||||||
|  |             }, response => { | ||||||
|  |                 this.$buefy.toast.open({ | ||||||
|  |                     message: "Failed to post quick entry!", | ||||||
|  |                     type: 'is-danger', | ||||||
|  |                     position: 'is-bottom', | ||||||
|  |                 }) | ||||||
|  |             }) | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         markReceivingComplete() { | ||||||
|  |             this.$http.post(`/api/receiving-batch/${this.batch.uuid}/mark-receiving-complete`).then(response => { | ||||||
|  |                 if (response.data.data) { | ||||||
|  |                     this.batch = response.data.data | ||||||
|  |                     this.$router.push('/receiving') | ||||||
|  |                 } else { | ||||||
|  |                     this.$buefy.toast.open({ | ||||||
|  |                         message: response.data.error || "Failed to mark receiving complete!", | ||||||
|  |                         type: 'is-danger', | ||||||
|  |                         position: 'is-bottom', | ||||||
|  |                     }) | ||||||
|  |                 } | ||||||
|  |             }, response => { | ||||||
|  |                 this.$buefy.toast.open({ | ||||||
|  |                     message: "Failed to mark receiving complete!", | ||||||
|  |                     type: 'is-danger', | ||||||
|  |                     position: 'is-bottom', | ||||||
|  |                 }) | ||||||
|  |             }) | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         receiveFromPO() { | ||||||
|  |             // here we will fetch all "eligible" purchase orders from backend, | ||||||
|  |             // so user can then choose one to receive from/against | ||||||
|  |             this.purchaseOrders = [] | ||||||
|  |             this.fetchingPurchaseOrders = true | ||||||
|  |             this.createMode = 'from_po' | ||||||
|  | 
 | ||||||
|  |             let url = '/api/receiving-batches/eligible-purchases' | ||||||
|  |             let params = { | ||||||
|  |                 'vendor_uuid': this.batch.vendor_uuid, | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.$http.get(url, {params: params}).then(response => { | ||||||
|  |                 if (response.data.purchases) { | ||||||
|  |                     this.purchaseOrders = response.data.purchases | ||||||
|  |                     this.fetchingPurchaseOrders = false | ||||||
|  |                 } else { | ||||||
|  |                     this.$buefy.toast.open({ | ||||||
|  |                         message: response.data.error || "Failed to fetch available purchases!", | ||||||
|  |                         type: 'is-danger', | ||||||
|  |                         position: 'is-bottom', | ||||||
|  |                     }) | ||||||
|  |                     this.fetchingPurchaseOrders = false | ||||||
|  |                 } | ||||||
|  |             }, response => { | ||||||
|  |                 this.$buefy.toast.open({ | ||||||
|  |                     message: "Failed to fetch available purchases!", | ||||||
|  |                     type: 'is-danger', | ||||||
|  |                     position: 'is-bottom', | ||||||
|  |                 }) | ||||||
|  |                 this.fetchingPurchaseOrders = false | ||||||
|  |             }) | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         receivePO(po) { | ||||||
|  |             // user has chosen a purchase order (PO) for which they wish to do | ||||||
|  |             // receiving.  so we create a new batch from that PO | ||||||
|  |             this.batch.purchase_key = po.key | ||||||
|  |             this.batch.description = "(from PO)" | ||||||
|  |             this.postingFormData = true | ||||||
|  |             this.save() | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         receiveFromScratch() { | ||||||
|  |             // if user intends to receive "from scratch" then we can | ||||||
|  |             // immediately create a new (empty) batch and send them to it | ||||||
|  |             this.batch.description = "(from scratch)" | ||||||
|  |             this.save() | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         getRowRoute(row) { | ||||||
|  |             return `/receiving/rows/${row.uuid}/receive` | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         rowFilters(uuid) { | ||||||
|  |             let filters = [{field: 'batch_uuid', op: 'eq', value: uuid}] | ||||||
|  |             filters = filters.concat(this.possibleRowFilters[this.selectedRowFilter]) | ||||||
|  |             return JSON.stringify(filters) | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         refreshData(record) { | ||||||
|  |             this.batch = record | ||||||
|  |             if (this.batch.complete) { | ||||||
|  |                 this.selectedRowFilter = 'all' | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         saveDisabled() { | ||||||
|  | 
 | ||||||
|  |             // vendor is required when creating | ||||||
|  |             if (this.mode == 'creating' && !this.batch.vendor_uuid) { | ||||||
|  |                 return true | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // save is allowed by default | ||||||
|  |             return false | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         save(url) { | ||||||
|  |             if (url === undefined) { | ||||||
|  |                 url = '/api/receiving-batches' | ||||||
|  |             } | ||||||
|  |             let params = { | ||||||
|  |                 vendor_uuid: this.batch.vendor_uuid, | ||||||
|  |                 description: this.batch.description, | ||||||
|  |                 notes: this.batch.notes, | ||||||
|  |             } | ||||||
|  |             if (this.mode == 'creating') { | ||||||
|  |                 params.purchase_key = this.batch.purchase_key | ||||||
|  |             } | ||||||
|  |             this.$http.post(url, params).then(response => { | ||||||
|  |                 if (response.data.data) { | ||||||
|  |                     this.$router.push('/receiving/' + response.data.data.uuid) | ||||||
|  |                 } else { | ||||||
|  |                     this.$buefy.toast.open({ | ||||||
|  |                         message: response.data.error || "Failed to save batch!", | ||||||
|  |                         type: 'is-danger', | ||||||
|  |                         position: 'is-bottom', | ||||||
|  |                     }) | ||||||
|  |                     this.postingFormData = false | ||||||
|  |                 } | ||||||
|  |             }, response => { | ||||||
|  |                 this.$buefy.toast.open({ | ||||||
|  |                     message: "Failed to save batch!", | ||||||
|  |                     type: 'is-danger', | ||||||
|  |                     position: 'is-bottom', | ||||||
|  |                 }) | ||||||
|  |                 this.postingFormData = false | ||||||
|  |             }) | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         // printSpecialOrderTickets() { | ||||||
|  |         //     let url = `/api/receiving-batch/${this.batch.uuid}/print-special-order-tickets` | ||||||
|  |         //     this.$http.post(url).then(response => { | ||||||
|  |         //         if (response.data.ok) { | ||||||
|  |         //             // must update our batch, so we know tickets were printed | ||||||
|  |         //             this.batch = response.data.batch | ||||||
|  |         //             this.$buefy.toast.open({ | ||||||
|  |         //                 message: `${response.data.printed} tickets were sent to the printer`, | ||||||
|  |         //                 type: 'is-success', | ||||||
|  |         //             }) | ||||||
|  | 
 | ||||||
|  |         //         } else { | ||||||
|  |         //             this.$buefy.toast.open({ | ||||||
|  |         //                 message: response.data.error || "Something went wrong!", | ||||||
|  |         //                 type: 'is-danger', | ||||||
|  |         //             }) | ||||||
|  |         //         } | ||||||
|  |         //     }, response => { | ||||||
|  |         //         this.$buefy.toast.open({ | ||||||
|  |         //             message: response.data.error, | ||||||
|  |         //             type: 'is-danger', | ||||||
|  |         //         }) | ||||||
|  |         //     }) | ||||||
|  |         // }, | ||||||
|  |     }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
							
								
								
									
										193
									
								
								mobile/src/views/receiving/ReceivingBatchRow.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								mobile/src/views/receiving/ReceivingBatchRow.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,193 @@ | ||||||
|  | <template> | ||||||
|  |   <byjove-model-crud model-name="ReceivingBatchRow" | ||||||
|  |                      model-index-title="Receiving" | ||||||
|  |                      :mode="mode" | ||||||
|  |                      :header-label-renderer="renderHeaderLabel" | ||||||
|  |                      :parent-header-label-renderer="renderParentHeaderLabel" | ||||||
|  |                      api-object-url="/api/receiving-batch-row/" | ||||||
|  |                      model-path-prefix="/receiving" | ||||||
|  |                      @refresh="record => { row = record }" | ||||||
|  |                      is-row | ||||||
|  |                      :allow-edit="false" | ||||||
|  |                      > | ||||||
|  |     <!-- TODO: allow-edit should be be true if batch still open --> | ||||||
|  |                      <!-- model-title="Ordering Batch" --> | ||||||
|  |                      <!-- model-title-plural="Ordering Batches" --> | ||||||
|  |                      <!-- api-index-url="/api/ordering-batches" --> | ||||||
|  |                      <!-- :row-label-renderer="renderRowLabel" --> | ||||||
|  |                      <!-- @save="save" --> | ||||||
|  | 
 | ||||||
|  |     <b-field label="UPC"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.upc_pretty }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Brand"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.brand_name }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Description"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.description }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Size"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.size }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Case Size"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.case_quantity }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Cases Ordered"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.cases_ordered }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Units Ordered"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.units_ordered }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Cases Shipped"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.cases_shipped }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Units Shipped"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.units_shipped }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Cases Received"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.cases_received }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Units Received"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.units_received }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Cases Damaged"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.cases_damaged }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Units Damaged"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.units_damaged }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Cases Expired"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.cases_expired }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Units Expired"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.units_expired }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field label="Status"> | ||||||
|  |       <span> | ||||||
|  |         {{ row.status_display }} | ||||||
|  |       </span> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <br /> | ||||||
|  | 
 | ||||||
|  |     <div class="buttons"> | ||||||
|  |       <b-button type="is-primary" | ||||||
|  |                 tag="router-link" | ||||||
|  |                 :to="`/receiving/rows/${row.uuid}/receive`"> | ||||||
|  |         Receive against this Row | ||||||
|  |       </b-button> | ||||||
|  |       <b-button type="is-primary" | ||||||
|  |                 tag="router-link" | ||||||
|  |                 :to="`/receiving/rows/${row.uuid}/edit`" | ||||||
|  |                 disabled> | ||||||
|  |         Edit this Row | ||||||
|  |       </b-button> | ||||||
|  |       <b-button type="is-danger" | ||||||
|  |                 disabled> | ||||||
|  |         Delete this Row | ||||||
|  |       </b-button>    </div> | ||||||
|  | 
 | ||||||
|  |   </byjove-model-crud> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import {ByjoveModelCrud} from 'byjove' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |     name: 'ReceivingBatchRow', | ||||||
|  |     props: { | ||||||
|  |         mode: String, | ||||||
|  |         allowCases: {           // TODO | ||||||
|  |             type: Boolean, | ||||||
|  |             default: true, | ||||||
|  |         }, | ||||||
|  |         allowExpired: {         // TODO | ||||||
|  |             type: Boolean, | ||||||
|  |             default: true, | ||||||
|  |         }, | ||||||
|  |         orderQuantitiesKnown: { // TODO | ||||||
|  |             type: Boolean, | ||||||
|  |             default: true, | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|  |     components: { | ||||||
|  |         ByjoveModelCrud, | ||||||
|  |     }, | ||||||
|  |     data: function() { | ||||||
|  |         return { | ||||||
|  |             row: {}, | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     methods: { | ||||||
|  | 
 | ||||||
|  |         renderParentHeaderLabel(row) { | ||||||
|  |             return row.batch_id_str | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         renderHeaderLabel(row) { | ||||||
|  |             return row.upc_pretty | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         // save(url) { | ||||||
|  |         //     let params = { | ||||||
|  |         //         item_id: this.product.item_id, | ||||||
|  |         //         description: this.product.description, | ||||||
|  |         //     } | ||||||
|  |         //     this.$http.post(url, params).then(response => { | ||||||
|  |         //         this.$router.push('/products/' + response.data.data.uuid) | ||||||
|  |         //     }) | ||||||
|  |         // }, | ||||||
|  |     }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <!-- <style> --> | ||||||
|  | <!--   table.receiving-quantities td { --> | ||||||
|  | <!--       padding: 0px 10px 0px 0px; --> | ||||||
|  | <!--   } --> | ||||||
|  | <!-- </style> --> | ||||||
							
								
								
									
										15
									
								
								mobile/src/views/receiving/ReceivingBatchRowReceive.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								mobile/src/views/receiving/ReceivingBatchRowReceive.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | <template> | ||||||
|  |   <byjove-receiving> | ||||||
|  |   </byjove-receiving> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import {ByjoveReceiving} from 'byjove' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |     name: 'ReceivingBatchRowReceive', | ||||||
|  |     components: { | ||||||
|  |         ByjoveReceiving, | ||||||
|  |     }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
							
								
								
									
										44
									
								
								mobile/src/views/receiving/ReceivingBatches.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								mobile/src/views/receiving/ReceivingBatches.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | ||||||
|  | <template> | ||||||
|  |   <byjove-model-index model-name="PurchaseBatch" | ||||||
|  |                       model-index-title="Receiving" | ||||||
|  |                       model-title="Receiving Batch" | ||||||
|  |                       model-title-plural="Receiving Batches" | ||||||
|  |                       model-path-prefix="/receiving" | ||||||
|  |                       model-permission-prefix="receiving" | ||||||
|  |                       api-index-url="/api/receiving-batches" | ||||||
|  |                       :api-index-sort="{field: 'id', dir: 'desc'}" | ||||||
|  |                       :api-index-filters="filters" | ||||||
|  |                       :label-renderer="renderLabel"> | ||||||
|  |   </byjove-model-index> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import {ByjoveModelIndex} from 'byjove' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |     name: 'ReceivingBatches', | ||||||
|  |     components: { | ||||||
|  |         ByjoveModelIndex, | ||||||
|  |     }, | ||||||
|  |     data() { | ||||||
|  |         return { | ||||||
|  |             filters: [ | ||||||
|  |                 {field: 'executed', op: 'is_null'}, | ||||||
|  |                 {or: [ | ||||||
|  |                     {field: 'receiving_complete', op: 'is_null'}, | ||||||
|  |                     {field: 'receiving_complete', op: 'eq', value: false}, | ||||||
|  |                 ]}, | ||||||
|  |                 {or: [ | ||||||
|  |                     {field: 'complete', op: 'is_null'}, | ||||||
|  |                     {field: 'complete', op: 'eq', value: false}, | ||||||
|  |                 ]}, | ||||||
|  |             ], | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     methods: { | ||||||
|  |         renderLabel(batch) { | ||||||
|  |             return `(${batch.id_str}) ${batch.vendor_display}; ${batch.description || ''} (${batch.rowcount} rows)` | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
							
								
								
									
										11
									
								
								mobile/src/views/receiving/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								mobile/src/views/receiving/index.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | import ReceivingBatches from './ReceivingBatches' | ||||||
|  | import ReceivingBatch from './ReceivingBatch' | ||||||
|  | import ReceivingBatchRow from './ReceivingBatchRow' | ||||||
|  | import ReceivingBatchRowReceive from './ReceivingBatchRowReceive' | ||||||
|  | 
 | ||||||
|  | export { | ||||||
|  |     ReceivingBatches, | ||||||
|  |     ReceivingBatch, | ||||||
|  |     ReceivingBatchRow, | ||||||
|  |     ReceivingBatchRowReceive, | ||||||
|  | } | ||||||
|  | @ -36,3 +36,4 @@ def includeme(config): | ||||||
| 
 | 
 | ||||||
|     # batches |     # batches | ||||||
|     config.include('tailbone.api.batch.ordering') |     config.include('tailbone.api.batch.ordering') | ||||||
|  |     config.include('tailbone.api.batch.receiving') | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar