|
|
@@ -0,0 +1,168 @@
|
|
|
+<template>
|
|
|
+ <q-dialog ref="dialogRef" @hide="onDialogHide">
|
|
|
+ <div style="width: 100%; max-width: 560px">
|
|
|
+ <q-card class="overflow-hidden" style="width: 100%">
|
|
|
+ <DefaultDialogHeader title="Ajustar Estoque" @close="onDialogCancel" />
|
|
|
+
|
|
|
+ <q-form @submit="onOKClick">
|
|
|
+ <q-card-section class="q-pt-sm">
|
|
|
+ <div class="text-h6 q-mb-sm">Produto</div>
|
|
|
+
|
|
|
+ <div class="row q-col-gutter-sm q-mb-md">
|
|
|
+ <DefaultInput
|
|
|
+ :model-value="product.name"
|
|
|
+ label="Nome do Produto"
|
|
|
+ class="col-12"
|
|
|
+ disable
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+
|
|
|
+ <DefaultInput
|
|
|
+ :model-value="product.description"
|
|
|
+ label="Descrição"
|
|
|
+ class="col-12"
|
|
|
+ type="textarea"
|
|
|
+ autogrow
|
|
|
+ disable
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+
|
|
|
+ <DefaultInput
|
|
|
+ :model-value="formatToBRLCurrency(product.price_sale)"
|
|
|
+ label="Valor Unitário"
|
|
|
+ class="col-6"
|
|
|
+ disable
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+
|
|
|
+ <DefaultInput
|
|
|
+ :model-value="String(product.quantity)"
|
|
|
+ label="Estoque Atual"
|
|
|
+ class="col-6"
|
|
|
+ disable
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-separator class="q-mb-md" />
|
|
|
+
|
|
|
+ <div class="text-h6 q-mb-sm">Movimentação</div>
|
|
|
+
|
|
|
+ <div class="row q-col-gutter-sm">
|
|
|
+ <DefaultSelect
|
|
|
+ v-model="form.type"
|
|
|
+ label="Tipo"
|
|
|
+ class="col-6"
|
|
|
+ :options="typeOptions"
|
|
|
+ emit-value
|
|
|
+ map-options
|
|
|
+ />
|
|
|
+
|
|
|
+ <DefaultInput
|
|
|
+ v-model="form.quantity"
|
|
|
+ label="Quantidade"
|
|
|
+ class="col-6"
|
|
|
+ type="number"
|
|
|
+ min="1"
|
|
|
+ :rules="[quantityRule]"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="stockError" class="text-negative text-caption q-mt-sm">
|
|
|
+ {{ stockError }}
|
|
|
+ </div>
|
|
|
+ </q-card-section>
|
|
|
+
|
|
|
+ <q-card-actions align="right" class="q-px-md q-pb-md">
|
|
|
+ <q-btn
|
|
|
+ label="Cancelar"
|
|
|
+ outline
|
|
|
+ color="primary-2"
|
|
|
+ @click="onDialogCancel"
|
|
|
+ />
|
|
|
+ <q-btn
|
|
|
+ label="Confirmar"
|
|
|
+ color="primary-2"
|
|
|
+ type="submit"
|
|
|
+ :loading="loading"
|
|
|
+ />
|
|
|
+ </q-card-actions>
|
|
|
+ </q-form>
|
|
|
+ </q-card>
|
|
|
+ </div>
|
|
|
+ </q-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, watch } from "vue";
|
|
|
+import { useDialogPluginComponent, useQuasar } from "quasar";
|
|
|
+import { adjustProductStock } from "src/api/product";
|
|
|
+import { formatToBRLCurrency } from "src/helpers/utils";
|
|
|
+
|
|
|
+import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
|
|
|
+import DefaultInput from "src/components/defaults/DefaultInput.vue";
|
|
|
+import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ product: {
|
|
|
+ type: Object,
|
|
|
+ required: true,
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+defineEmits([...useDialogPluginComponent.emits]);
|
|
|
+
|
|
|
+const $q = useQuasar();
|
|
|
+const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
|
+ useDialogPluginComponent();
|
|
|
+
|
|
|
+const loading = ref(false);
|
|
|
+const stockError = ref(null);
|
|
|
+
|
|
|
+const typeOptions = [
|
|
|
+ { label: "Entrada", value: "entrada" },
|
|
|
+ { label: "Saída", value: "saida" },
|
|
|
+];
|
|
|
+
|
|
|
+const form = ref({
|
|
|
+ type: "entrada",
|
|
|
+ quantity: 1,
|
|
|
+});
|
|
|
+
|
|
|
+watch(() => [form.value.type, form.value.quantity], () => {
|
|
|
+ stockError.value = null;
|
|
|
+});
|
|
|
+
|
|
|
+const quantityRule = (val) => {
|
|
|
+ const qty = Number(val);
|
|
|
+ if (!qty || qty < 1) return "Informe uma quantidade válida.";
|
|
|
+ if (form.value.type === "saida" && qty > props.product.quantity) {
|
|
|
+ return `Estoque insuficiente. Disponível: ${props.product.quantity}`;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+};
|
|
|
+
|
|
|
+const onOKClick = async () => {
|
|
|
+ const qty = Number(form.value.quantity);
|
|
|
+
|
|
|
+ if (form.value.type === "saida" && qty > props.product.quantity) {
|
|
|
+ stockError.value = `Estoque insuficiente. Disponível: ${props.product.quantity}`;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const updated = await adjustProductStock(props.product.id, {
|
|
|
+ type: form.value.type,
|
|
|
+ quantity: qty,
|
|
|
+ });
|
|
|
+ onDialogOK(updated);
|
|
|
+ } catch (err) {
|
|
|
+ const msg = err?.response?.data?.message ?? "Erro ao ajustar estoque.";
|
|
|
+ stockError.value = msg;
|
|
|
+ $q.notify({ type: "negative", message: msg });
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|