Venta de admin que puede editar precios de productos #15
							
								
								
									
										341
									
								
								src/components/AdminPurchase.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								src/components/AdminPurchase.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,341 @@ | |||||||
|  | <template> | ||||||
|  | <v-container> | ||||||
|  |   <v-form ref="purchase" v-model="valid" @change="onFormChange"> | ||||||
|  |     <v-row> | ||||||
|  |         <v-col> | ||||||
|  |             <v-autocomplete | ||||||
|  |                 v-model="purchase.customer" | ||||||
|  |                 :items="filteredClients" | ||||||
|  |                 :search="client_search" | ||||||
|  |                 no-data-text="No se hallaron clientes" | ||||||
|  |                 item-title="name" | ||||||
|  |                 item-value="id" | ||||||
|  |                 @update:model-value="onFormChange" | ||||||
|  |                 label="Cliente" | ||||||
|  |                 :rules="[rules.required]" | ||||||
|  |                 required | ||||||
|  |                 class="mr-4" | ||||||
|  |             ></v-autocomplete> | ||||||
|  |             <v-btn color="primary" @click="openModal">Agregar Cliente</v-btn> | ||||||
|  |             <CreateCustomerModal ref="customerModal" @customerCreated="handleNewCustomer"/> | ||||||
|  |       </v-col> | ||||||
|  |       <v-col lg="4"> | ||||||
|  |           <v-text-field | ||||||
|  |               v-model="purchase.date" | ||||||
|  |               label="Fecha" | ||||||
|  |               type="datetime-local" | ||||||
|  |               :rules="[rules.required]" | ||||||
|  |               required | ||||||
|  |               readonly | ||||||
|  |           ></v-text-field> | ||||||
|  |       </v-col> | ||||||
|  |      </v-row> | ||||||
|  |     <v-textarea | ||||||
|  |       v-model="purchase.notes" | ||||||
|  |       label="Notas" | ||||||
|  |       rows="2" | ||||||
|  |       ></v-textarea> | ||||||
|  |     <v-divider></v-divider> | ||||||
|  |     <v-container> | ||||||
|  |       <v-toolbar> | ||||||
|  |         <v-toolbar-title secondary>Productos</v-toolbar-title> | ||||||
|  | p      </v-toolbar> | ||||||
|  |       <v-container v-for="(line, index) in purchase.saleline_set" :key="line.id"> | ||||||
|  |         <v-row> | ||||||
|  |             <v-col | ||||||
|  |                 lg="9"> | ||||||
|  |             <v-autocomplete | ||||||
|  |               v-model="line.product" | ||||||
|  |               :items="filteredProducts" | ||||||
|  |               :search="product_search" | ||||||
|  |               @update:modelValue="onProductChange(index)" | ||||||
|  |               no-data-text="No se hallaron productos" | ||||||
|  |               item-title="name" | ||||||
|  |               item-value="id" | ||||||
|  |               item-subtitle="Price" | ||||||
|  |               label="Producto" | ||||||
|  |               :rules="[rules.required]" | ||||||
|  |               required | ||||||
|  |               > | ||||||
|  |               <template v-slot:item="{ props, item }"> | ||||||
|  |                 <v-list-item v-bind="props" :title="item.raw.name" :subtitle="formatPrice(item.raw.price)"></v-list-item> | ||||||
|  |               </template> | ||||||
|  |             </v-autocomplete> | ||||||
|  |           </v-col> | ||||||
|  |           <v-col | ||||||
|  |               lg="2" | ||||||
|  |           > | ||||||
|  |               <v-text-field | ||||||
|  |                   v-model.number="line.quantity" | ||||||
|  |                   label="Cantidad" | ||||||
|  |                   type="number" | ||||||
|  |                   :rules="[rules.required,rules.positive]" | ||||||
|  |                   required | ||||||
|  |               ></v-text-field> | ||||||
|  |           </v-col> | ||||||
|  |         </v-row> | ||||||
|  |         <v-row> | ||||||
|  |           <v-col> | ||||||
|  |             <v-text-field | ||||||
|  |               v-model.number="line.unit_price" | ||||||
|  |               label="Precio" | ||||||
|  |               type="number" | ||||||
|  |               :rules="[rules.required]" | ||||||
|  |               prefix="$" | ||||||
|  |               required | ||||||
|  |               ></v-text-field> | ||||||
|  |           </v-col> | ||||||
|  |           <v-col> | ||||||
|  |             <v-text-field | ||||||
|  |               v-model="line.measuring_unit" | ||||||
|  |               label="UdM" | ||||||
|  |               persistent-placeholder="true" | ||||||
|  |               readonly | ||||||
|  |               ></v-text-field> | ||||||
|  |           </v-col> | ||||||
|  |           <v-col> | ||||||
|  |             <v-text-field | ||||||
|  |               type="number" | ||||||
|  |               :value="calculateSubtotal(line)" | ||||||
|  |               label="Subtotal" | ||||||
|  |               prefix="$" | ||||||
|  |               readonly | ||||||
|  |                 disable | ||||||
|  |                 persistent-placeholder="true" | ||||||
|  |               ></v-text-field> | ||||||
|  |             </v-col> | ||||||
|  |             <v-col> | ||||||
|  |               <v-btn @click="removeLine(index)" color="red">Eliminar</v-btn> | ||||||
|  |             </v-col> | ||||||
|  |         </v-row> | ||||||
|  |         <v-alert type="warning" :duration="2000" closable v-model="show_alert_lines"> | ||||||
|  |           No se puede eliminar la única línea. | ||||||
|  |         </v-alert> | ||||||
|  |       </v-container> | ||||||
|  |         <v-btn @click="addLine" color="blue">Agregar</v-btn> | ||||||
|  |         </v-container> | ||||||
|  |         <v-divider></v-divider> | ||||||
|  |         <v-text-field | ||||||
|  |           :value="calculateTotal" | ||||||
|  |           label="Total" | ||||||
|  |           prefix="$" | ||||||
|  |           readonly | ||||||
|  |           persistent-placeholder="true" | ||||||
|  |         ></v-text-field> | ||||||
|  |         <v-container v-if="calculateTotal > 0"> | ||||||
|  |           <v-select | ||||||
|  |             :items="payment_methods" | ||||||
|  |             v-model="purchase.payment_method" | ||||||
|  |             item-title="text" | ||||||
|  |             item-value="value" | ||||||
|  |             label="Pago en" | ||||||
|  |             :rules="[rules.required]" | ||||||
|  |             required | ||||||
|  |           ></v-select> | ||||||
|  |           <v-btn @click="openCasherModal" v-if="purchase.payment_method === 'CASH'">Calcular Devuelta</v-btn> | ||||||
|  |           <CasherModal :total_purchase="calculateTotal" ref="casherModal"</CasherModal> | ||||||
|  |         </v-container> | ||||||
|  |         <v-btn @click="submit" color="green">Comprar</v-btn> | ||||||
|  |         <v-alert type="error" :duration="2000" closable v-model="show_alert_purchase"> | ||||||
|  |           Verifique los campos obligatorios. | ||||||
|  |         </v-alert> | ||||||
|  |   </v-form> | ||||||
|  | </v-container> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  |   import CustomerForm from './CreateCustomerModal.vue'; | ||||||
|  |   import CasherModal from './CasherModal.vue'; | ||||||
|  |   import { inject } from 'vue'; | ||||||
|  |  | ||||||
|  |   export default { | ||||||
|  |     name: 'DonConfiao', | ||||||
|  |     components: { | ||||||
|  |      CustomerForm, | ||||||
|  |      CasherModal, | ||||||
|  |    }, | ||||||
|  |    props: { | ||||||
|  |      msg: String | ||||||
|  |    }, | ||||||
|  |    data() { | ||||||
|  |      return { | ||||||
|  |        api: inject('api'), | ||||||
|  |        valid: false, | ||||||
|  |        form_changed: false, | ||||||
|  |        show_alert_lines: false, | ||||||
|  |        show_alert_purchase: false, | ||||||
|  |        client_search: '', | ||||||
|  |        product_search: '', | ||||||
|  |        payment_methods: null, | ||||||
|  |        purchase: { | ||||||
|  |          date: this.getCurrentDate(), | ||||||
|  |          customer: null, | ||||||
|  |          notes: '', | ||||||
|  |          payment_method: null, | ||||||
|  |          saleline_set: [{product:'', unit_price: 0, quantity: 0, unit: ''}], | ||||||
|  |        }, | ||||||
|  |        rules: { | ||||||
|  |            required: value => !!value || 'Requerido.', | ||||||
|  |            positive: value => value > 0 || 'La cantidad debe ser mayor que 0.', | ||||||
|  |        }, | ||||||
|  |          menuItems: [ | ||||||
|  |              { title: 'Inicio', route: '/'}, | ||||||
|  |              { title: 'Compras', route:'/compras'}, | ||||||
|  |          ], | ||||||
|  |          clients: [], | ||||||
|  |          products: [], | ||||||
|  |      }; | ||||||
|  |    }, | ||||||
|  |      created() { | ||||||
|  |      this.fetchClients(); | ||||||
|  |      this.fetchProducts(); | ||||||
|  |      this.fetchPaymentMethods(); | ||||||
|  |    }, | ||||||
|  |    watch: { | ||||||
|  |      group () { | ||||||
|  |        this.drawer = false | ||||||
|  |      }, | ||||||
|  |    }, | ||||||
|  |    beforeMount() { | ||||||
|  |       window.addEventListener('beforeunload', this.confirmLeave); | ||||||
|  |    }, | ||||||
|  |    beforeDestroy() { | ||||||
|  |       window.removeEventListener('beforeunload', this.confirmLeave); | ||||||
|  |    }, | ||||||
|  |    computed: { | ||||||
|  |      calculateTotal() { | ||||||
|  |        return this.purchase.saleline_set.reduce((total, saleline) => { | ||||||
|  |          return total + this.calculateSubtotal(saleline); | ||||||
|  |        }, 0); | ||||||
|  |      }, | ||||||
|  |      filteredClients() { | ||||||
|  |        return this.clients.filter(client => { | ||||||
|  |          if (this.client_search === '') { | ||||||
|  |            return []; | ||||||
|  |          } else { | ||||||
|  |            return client.name.toLowerCase().includes(this.client_search.toLowerCase()); | ||||||
|  |          } | ||||||
|  |        }); | ||||||
|  |      }, | ||||||
|  |      filteredProducts() { | ||||||
|  |        return this.products.filter(product => { | ||||||
|  |          if (this.product_search === '') { | ||||||
|  |            return []; | ||||||
|  |          } else { | ||||||
|  |            return product.name.toLowerCase().includes(this.product_search.toLowerCase()); | ||||||
|  |          } | ||||||
|  |        }); | ||||||
|  |      }, | ||||||
|  |    }, | ||||||
|  |    methods: { | ||||||
|  |      openModal() { | ||||||
|  |       this.$refs.customerModal.openModal(); | ||||||
|  |      }, | ||||||
|  |      onFormChange() { | ||||||
|  |        this.form_changed = true; | ||||||
|  |      }, | ||||||
|  |      openCasherModal() { | ||||||
|  |        this.$refs.casherModal.dialog = true | ||||||
|  |      }, | ||||||
|  |      confirmLeave(event) { | ||||||
|  |        if (this.form_changed) { | ||||||
|  |          const message = '¿seguro que quieres salir? Perderas la información diligenciada'; | ||||||
|  |          event.preventDefault(); | ||||||
|  |          event.returnValue = message; | ||||||
|  |          return message; | ||||||
|  |        } | ||||||
|  |      }, | ||||||
|  |      getCurrentDate() { | ||||||
|  |          const today = new Date(); | ||||||
|  |          const gmtOffSet = -5; | ||||||
|  |          const localDate = new Date(today.getTime() + (gmtOffSet * 60 * 60 * 1000)); | ||||||
|  |          // Formatear la fecha y hora en el formato YYYY-MM-DDTHH:MM | ||||||
|  |          const formattedDate = localDate.toISOString().slice(0,16); | ||||||
|  |          return formattedDate; | ||||||
|  |      }, | ||||||
|  |        onProductChange(index) { | ||||||
|  |            const selectedProductId = this.purchase.saleline_set[index].product; | ||||||
|  |        const selectedProduct = this.products.find(p => p.id == selectedProductId); | ||||||
|  |          this.purchase.saleline_set[index].unit_price = selectedProduct.price; | ||||||
|  |          this.purchase.saleline_set[index].measuring_unit = selectedProduct.measuring_unit; | ||||||
|  |      }, | ||||||
|  |      fetchClients() { | ||||||
|  |        this.api.getCustomers() | ||||||
|  |            .then(data => { | ||||||
|  |              this.clients = data; | ||||||
|  |            }) | ||||||
|  |            .catch(error => { | ||||||
|  |              console.error(error); | ||||||
|  |            }); | ||||||
|  |      }, | ||||||
|  |        handleNewCustomer(newCustomer){ | ||||||
|  |            this.clients.push(newCustomer); | ||||||
|  |            this.purchase.customer = newCustomer.id; | ||||||
|  |        }, | ||||||
|  |      fetchProducts() { | ||||||
|  |        this.api.getProducts() | ||||||
|  |            .then(data => { | ||||||
|  |              this.products = data; | ||||||
|  |            }) | ||||||
|  |            .catch(error => { | ||||||
|  |              console.error(error); | ||||||
|  |            }); | ||||||
|  |      }, | ||||||
|  |      fetchPaymentMethods() { | ||||||
|  |        this.api.getPaymentMethods() | ||||||
|  |            .then(data => { | ||||||
|  |              this.payment_methods = data; | ||||||
|  |            }) | ||||||
|  |            .catch(error => { | ||||||
|  |              console.error(error); | ||||||
|  |            }); | ||||||
|  |      }, | ||||||
|  |      addLine() { | ||||||
|  |        this.purchase.saleline_set.push({ product: '', unit_price: 0, quantity:0, measuring_unit: ''}); | ||||||
|  |      }, | ||||||
|  |      removeLine(index) { | ||||||
|  |          if (this.purchase.saleline_set.length > 1) { | ||||||
|  |              this.purchase.saleline_set.splice(index, 1); | ||||||
|  |          } else { | ||||||
|  |            this.show_alert_lines = true; | ||||||
|  |            setTimeout(() => { | ||||||
|  |              this.show_alert_lines = false; | ||||||
|  |            }, 2000); | ||||||
|  |          } | ||||||
|  |      }, | ||||||
|  |      calculateSubtotal(line) { | ||||||
|  |        return line.unit_price * line.quantity; | ||||||
|  |      }, | ||||||
|  |      async submit() { | ||||||
|  |        this.$refs.purchase.validate(); | ||||||
|  |          if (this.valid) { | ||||||
|  |            this.api.createPurchase(this.purchase) | ||||||
|  |                .then(data => { | ||||||
|  |                  console.log('Compra enviada:', data); | ||||||
|  |                  this.$router.push({ | ||||||
|  |                    path: "/summary_purchase", | ||||||
|  |                    query : {id: parseInt(data.id)} | ||||||
|  |                  }); | ||||||
|  |                }) | ||||||
|  |            .catch(error => console.error('Error al enviarl la compra:', error)); | ||||||
|  |        } else { | ||||||
|  |          this.show_alert_purchase = true; | ||||||
|  |            setTimeout(() => { | ||||||
|  |              this.show_alert_purchase = false; | ||||||
|  |            }, 4000); | ||||||
|  |        } | ||||||
|  |      }, | ||||||
|  |      navigate(route) { | ||||||
|  |        this.$router.push(route); | ||||||
|  |      }, | ||||||
|  |      formatPrice(price) { | ||||||
|  |        return new Intl.NumberFormat('es-ES', { style: 'currency', currency: 'COP' }).format(price); | ||||||
|  |      }, | ||||||
|  |    }, | ||||||
|  |      mounted() { | ||||||
|  |          this.fetchClients(); | ||||||
|  |      } | ||||||
|  |  }; | ||||||
|  | </script> | ||||||
|  | <style> | ||||||
|  | </style> | ||||||
| @@ -12,9 +12,28 @@ | |||||||
|   <v-navigation-drawer v-model="drawer" |   <v-navigation-drawer v-model="drawer" | ||||||
|                        :location="$vuetify.display.mobile ? 'bottom' : undefined" |                        :location="$vuetify.display.mobile ? 'bottom' : undefined" | ||||||
|                        temporary> |                        temporary> | ||||||
|       <v-list> |     <v-list | ||||||
|           <v-list-item v-for="item in menuItems" :key="item.title" @click="navigate(item.route)"> |       density="compact" | ||||||
|               <v-list-item-title>{{ item.title }}</v-list-item-title> |       nav | ||||||
|  |     > | ||||||
|  |       <v-list-item | ||||||
|  |         v-for="item in menuItems" | ||||||
|  |         :key="item.title" | ||||||
|  |         :title="item.title" | ||||||
|  |         :prepend-icon="item.icon" | ||||||
|  |         @click="navigate(item.route)" | ||||||
|  |       ></v-list-item> | ||||||
|  |       <v-list-item prepend-icon="mdi-cog" title="Administracion" @click="toggleAdminMenu()"></v-list-item> | ||||||
|  |       <v-list-item> | ||||||
|  |         <v-list v-if="showAdminMenu"> | ||||||
|  |           <v-list-item | ||||||
|  |             v-for="item in menuAdminItems" | ||||||
|  |             :key="item.title" | ||||||
|  |             :title="item.title" | ||||||
|  |             :prepend-icon="item.icon" | ||||||
|  |             @click="navigateAdmin(item.route)" | ||||||
|  |           ></v-list-item> | ||||||
|  |         </v-list> | ||||||
|       </v-list-item> |       </v-list-item> | ||||||
|     </v-list> |     </v-list> | ||||||
|   </v-navigation-drawer> |   </v-navigation-drawer> | ||||||
| @@ -26,12 +45,16 @@ | |||||||
|      data: () => ({ |      data: () => ({ | ||||||
|          drawer: false, |          drawer: false, | ||||||
|          group: null, |          group: null, | ||||||
|  |          showAdminMenu: false, | ||||||
|          menuItems: [ |          menuItems: [ | ||||||
|              { title: 'Inicio', route: '/'}, |              { title: 'Inicio', route: '/', icon: 'mdi-home'}, | ||||||
|              { title: 'Comprar', route:'/comprar'}, |              { title: 'Comprar', route:'/comprar', icon: 'mdi-cart'}, | ||||||
|              { title: 'Cuadrar tarro', route: '/cuadrar_tarro'}, |          ], | ||||||
|              { title: 'Cuadres de tarro', route: '/cuadres_de_tarro'}, |          menuAdminItems: [ | ||||||
|              { title: 'CSV Tryton', route: '/ventas_para_tryton'}, |              { title: 'Cuadrar tarro', route: '/cuadrar_tarro', icon: 'mdi-calculator'}, | ||||||
|  |              { title: 'Cuadres de tarro', route: '/cuadres_de_tarro', icon: 'mdi-chart-bar'}, | ||||||
|  |              { title: 'CSV Tryton', route: '/ventas_para_tryton', icon: 'mdi-file-table'}, | ||||||
|  |              { title: 'Compra adm', route: '/compra_admin', icon: 'mdi-cart'}, | ||||||
|          ], |          ], | ||||||
|      }), |      }), | ||||||
|      watch: { |      watch: { | ||||||
| @@ -43,6 +66,13 @@ | |||||||
|          navigate(route) { |          navigate(route) { | ||||||
|              this.$router.push(route); |              this.$router.push(route); | ||||||
|          }, |          }, | ||||||
|  |          navigateAdmin(route) { | ||||||
|  |              this.toggleAdminMenu(); | ||||||
|  |              this.navigate(route); | ||||||
|  |          }, | ||||||
|  |          toggleAdminMenu() { | ||||||
|  |              this.showAdminMenu = !this.showAdminMenu; | ||||||
|  |          }, | ||||||
|      } |      } | ||||||
|   } |   } | ||||||
| </script> | </script> | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/pages/compra_admin.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/pages/compra_admin.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <CodeDialog @code-verified="(verified) => showComponent = verified"/> | ||||||
|  |   </div> | ||||||
|  |   <AdminPurchase v-if="showComponent"/> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script > | ||||||
|  |   import CodeDialog from '../components/CodeDialog.vue' | ||||||
|  |  | ||||||
|  |   export default { | ||||||
|  |     data() { | ||||||
|  |       return { | ||||||
|  |         showComponent: false, | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     components: { CodeDialog }, | ||||||
|  |     methods: {}, | ||||||
|  |   } | ||||||
|  | </script> | ||||||
		Reference in New Issue
	
	Block a user