|
@@ -3,7 +3,7 @@
|
|
|
<div style="width: 100%; max-width: 700px">
|
|
<div style="width: 100%; max-width: 700px">
|
|
|
<q-card class="overflow-hidden" style="width: 100%">
|
|
<q-card class="overflow-hidden" style="width: 100%">
|
|
|
<DefaultDialogHeader
|
|
<DefaultDialogHeader
|
|
|
- :title="props.package ? 'Editar Pacote' : 'Novo Pacote'"
|
|
|
|
|
|
|
+ :title="props.package ? 'Editar Proposta Comercial' : 'Nova Proposta Comercial'"
|
|
|
@close="onDialogCancel"
|
|
@close="onDialogCancel"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
@@ -17,7 +17,7 @@
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<div
|
|
<div
|
|
|
- v-show="currentTab === 'informacoes'"
|
|
|
|
|
|
|
+ v-show="currentTab === 'dados'"
|
|
|
class="col-12"
|
|
class="col-12"
|
|
|
>
|
|
>
|
|
|
<div class="row q-col-gutter-md">
|
|
<div class="row q-col-gutter-md">
|
|
@@ -193,300 +193,24 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div
|
|
<div
|
|
|
- v-show="currentTab === 'unidades'"
|
|
|
|
|
|
|
+ v-show="currentTab === 'pavao'"
|
|
|
class="col-12"
|
|
class="col-12"
|
|
|
>
|
|
>
|
|
|
- <div class="column q-gutter-y-sm">
|
|
|
|
|
- <q-input
|
|
|
|
|
- bg-color="white"
|
|
|
|
|
- color="secondary"
|
|
|
|
|
- dense
|
|
|
|
|
- label="Visível para todas as Unidades / Grupos"
|
|
|
|
|
- label-color="secondary"
|
|
|
|
|
- outlined
|
|
|
|
|
- readonly
|
|
|
|
|
- style="border-radius: 8px"
|
|
|
|
|
- >
|
|
|
|
|
- <template #append>
|
|
|
|
|
- <q-btn
|
|
|
|
|
- color="positive"
|
|
|
|
|
- dense
|
|
|
|
|
- flat
|
|
|
|
|
- icon="mdi-check"
|
|
|
|
|
- round
|
|
|
|
|
- size="sm"
|
|
|
|
|
- @click="setAllVisible(true)"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <q-btn
|
|
|
|
|
- color="negative"
|
|
|
|
|
- dense
|
|
|
|
|
- flat
|
|
|
|
|
- icon="mdi-close-circle-outline"
|
|
|
|
|
- round
|
|
|
|
|
- size="sm"
|
|
|
|
|
- @click="setAllVisible(false)"
|
|
|
|
|
- />
|
|
|
|
|
- </template>
|
|
|
|
|
- </q-input>
|
|
|
|
|
-
|
|
|
|
|
- <div class="row items-center q-gutter-x-sm">
|
|
|
|
|
- <q-input
|
|
|
|
|
- v-model="unitSearch"
|
|
|
|
|
- bg-color="white"
|
|
|
|
|
- class="col"
|
|
|
|
|
- color="secondary"
|
|
|
|
|
- dense
|
|
|
|
|
- label="Buscar Unidade / Grupo"
|
|
|
|
|
- label-color="secondary"
|
|
|
|
|
- outlined
|
|
|
|
|
- style="border-radius: 8px"
|
|
|
|
|
- >
|
|
|
|
|
- <template #append>
|
|
|
|
|
- <q-icon color="secondary" name="mdi-magnify" />
|
|
|
|
|
- </template>
|
|
|
|
|
- </q-input>
|
|
|
|
|
-
|
|
|
|
|
- <q-btn
|
|
|
|
|
- color="primary"
|
|
|
|
|
- icon="mdi-plus"
|
|
|
|
|
- style="
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- height: 40px;
|
|
|
|
|
- width: 40px;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- "
|
|
|
|
|
- unelevated
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <q-scroll-area
|
|
|
|
|
- class="rounded-borders"
|
|
|
|
|
- style="height: 280px; background-color: #f5f5f5"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="q-pa-sm">
|
|
|
|
|
- <div
|
|
|
|
|
- v-if="loadingUnits"
|
|
|
|
|
- class="flex flex-center q-py-md"
|
|
|
|
|
- >
|
|
|
|
|
- <q-spinner color="secondary" size="sm" />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <template v-else>
|
|
|
|
|
- <div class="row items-center q-mb-xs q-px-sm">
|
|
|
|
|
- <q-icon
|
|
|
|
|
- class="q-mr-xs"
|
|
|
|
|
- color="secondary"
|
|
|
|
|
- name="mdi-account-multiple"
|
|
|
|
|
- size="sm"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <span class="text-body2">
|
|
|
|
|
- Unidades Ativas ({{ filteredUnits.length }})
|
|
|
|
|
- </span>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <q-separator class="q-mb-xs" />
|
|
|
|
|
-
|
|
|
|
|
- <div class="row q-px-sm q-py-xs">
|
|
|
|
|
- <span class="col text-caption text-grey-6">
|
|
|
|
|
- Unidade
|
|
|
|
|
- </span>
|
|
|
|
|
-
|
|
|
|
|
- <span class="col-auto text-caption text-grey-6">
|
|
|
|
|
- Status
|
|
|
|
|
- </span>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <template v-for="unit in filteredUnits" :key="unit.id">
|
|
|
|
|
- <q-separator />
|
|
|
|
|
-
|
|
|
|
|
- <div class="row items-center q-px-sm q-py-sm">
|
|
|
|
|
- <span
|
|
|
|
|
- class="col text-body2 row items-center q-gutter-x-xs"
|
|
|
|
|
- >
|
|
|
|
|
- {{ formatUnitName(unit) }}
|
|
|
|
|
-
|
|
|
|
|
- <q-icon
|
|
|
|
|
- v-if="lockedUnitIds.has(unit.id)"
|
|
|
|
|
- color="amber-7"
|
|
|
|
|
- name="mdi-lock"
|
|
|
|
|
- size="xs"
|
|
|
|
|
- >
|
|
|
|
|
- <q-tooltip>
|
|
|
|
|
- Visibilidade definida por grupo
|
|
|
|
|
- </q-tooltip>
|
|
|
|
|
- </q-icon>
|
|
|
|
|
- </span>
|
|
|
|
|
-
|
|
|
|
|
- <div class="col-auto row q-gutter-x-xs">
|
|
|
|
|
- <q-btn
|
|
|
|
|
- icon="mdi-check"
|
|
|
|
|
- round
|
|
|
|
|
- size="xs"
|
|
|
|
|
- unelevated
|
|
|
|
|
- :color="unit.visible ? 'positive' : 'grey-4'"
|
|
|
|
|
- @click="unit.visible = true"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <q-btn
|
|
|
|
|
- color="negative"
|
|
|
|
|
- icon="mdi-close"
|
|
|
|
|
- round
|
|
|
|
|
- size="xs"
|
|
|
|
|
- :disable="lockedUnitIds.has(unit.id)"
|
|
|
|
|
- :outline="unit.visible"
|
|
|
|
|
- :unelevated="!unit.visible"
|
|
|
|
|
- @click="
|
|
|
|
|
- !lockedUnitIds.has(unit.id) &&
|
|
|
|
|
- (unit.visible = false)
|
|
|
|
|
- "
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </template>
|
|
|
|
|
- </template>
|
|
|
|
|
- </div>
|
|
|
|
|
- </q-scroll-area>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <ProposalPricingTab
|
|
|
|
|
+ v-model="form.pavao"
|
|
|
|
|
+ :product-options="productOptions"
|
|
|
|
|
+ />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div
|
|
<div
|
|
|
- v-show="currentTab === 'grupos'"
|
|
|
|
|
|
|
+ v-show="currentTab === 'irrecusavel'"
|
|
|
class="col-12"
|
|
class="col-12"
|
|
|
>
|
|
>
|
|
|
- <div class="column q-gutter-y-sm">
|
|
|
|
|
- <q-input
|
|
|
|
|
- bg-color="white"
|
|
|
|
|
- color="secondary"
|
|
|
|
|
- dense
|
|
|
|
|
- label="Visível Para todos os Grupos"
|
|
|
|
|
- label-color="secondary"
|
|
|
|
|
- outlined
|
|
|
|
|
- readonly
|
|
|
|
|
- style="border-radius: 8px"
|
|
|
|
|
- >
|
|
|
|
|
- <template #append>
|
|
|
|
|
- <q-btn
|
|
|
|
|
- color="positive"
|
|
|
|
|
- dense
|
|
|
|
|
- flat
|
|
|
|
|
- icon="mdi-check"
|
|
|
|
|
- round
|
|
|
|
|
- size="sm"
|
|
|
|
|
- @click="setAllGroupsVisible(true)"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <q-btn
|
|
|
|
|
- color="negative"
|
|
|
|
|
- dense
|
|
|
|
|
- flat
|
|
|
|
|
- icon="mdi-close-circle-outline"
|
|
|
|
|
- round
|
|
|
|
|
- size="sm"
|
|
|
|
|
- @click="setAllGroupsVisible(false)"
|
|
|
|
|
- />
|
|
|
|
|
- </template>
|
|
|
|
|
- </q-input>
|
|
|
|
|
-
|
|
|
|
|
- <q-input
|
|
|
|
|
- v-model="groupSearch"
|
|
|
|
|
- bg-color="white"
|
|
|
|
|
- color="secondary"
|
|
|
|
|
- dense
|
|
|
|
|
- label="Buscar Grupo"
|
|
|
|
|
- label-color="secondary"
|
|
|
|
|
- outlined
|
|
|
|
|
- style="border-radius: 8px"
|
|
|
|
|
- >
|
|
|
|
|
- <template #append>
|
|
|
|
|
- <q-icon color="secondary" name="mdi-magnify" />
|
|
|
|
|
- </template>
|
|
|
|
|
- </q-input>
|
|
|
|
|
-
|
|
|
|
|
- <q-scroll-area
|
|
|
|
|
- class="rounded-borders"
|
|
|
|
|
- style="height: 280px; background-color: #f5f5f5"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="q-pa-sm">
|
|
|
|
|
- <div
|
|
|
|
|
- v-if="loadingGroups"
|
|
|
|
|
- class="flex flex-center q-py-md"
|
|
|
|
|
- >
|
|
|
|
|
- <q-spinner color="secondary" size="sm" />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <template v-else>
|
|
|
|
|
- <div class="row items-center q-mb-xs q-px-sm">
|
|
|
|
|
- <q-icon
|
|
|
|
|
- class="q-mr-xs"
|
|
|
|
|
- color="secondary"
|
|
|
|
|
- name="mdi-account-group"
|
|
|
|
|
- size="sm"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <span class="text-body2">
|
|
|
|
|
- Grupos ({{ filteredGroups.length }})
|
|
|
|
|
- </span>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <q-separator class="q-mb-xs" />
|
|
|
|
|
-
|
|
|
|
|
- <div class="row q-px-sm q-py-xs">
|
|
|
|
|
- <span class="col text-caption text-grey-6">
|
|
|
|
|
- Grupo
|
|
|
|
|
- </span>
|
|
|
|
|
-
|
|
|
|
|
- <span class="col-6 text-caption text-grey-6">
|
|
|
|
|
- Unidades
|
|
|
|
|
- </span>
|
|
|
|
|
-
|
|
|
|
|
- <span class="col-auto text-caption text-grey-6">
|
|
|
|
|
- Status
|
|
|
|
|
- </span>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <template
|
|
|
|
|
- v-for="group in filteredGroups"
|
|
|
|
|
- :key="group.id"
|
|
|
|
|
- >
|
|
|
|
|
- <q-separator />
|
|
|
|
|
-
|
|
|
|
|
- <div class="row items-center q-px-sm q-py-sm">
|
|
|
|
|
- <span class="col text-body2">
|
|
|
|
|
- {{ group.name }}
|
|
|
|
|
- </span>
|
|
|
|
|
-
|
|
|
|
|
- <span class="col-6 text-caption text-grey-6">
|
|
|
|
|
- {{ group.unit_ids?.length ?? 0 }} unidade(s)
|
|
|
|
|
- </span>
|
|
|
|
|
-
|
|
|
|
|
- <div class="col-auto row q-gutter-x-xs">
|
|
|
|
|
- <q-btn
|
|
|
|
|
- icon="mdi-check"
|
|
|
|
|
- round
|
|
|
|
|
- size="xs"
|
|
|
|
|
- unelevated
|
|
|
|
|
- :color="group.visible ? 'positive' : 'grey-4'"
|
|
|
|
|
- @click="group.visible = true"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <q-btn
|
|
|
|
|
- color="negative"
|
|
|
|
|
- icon="mdi-close"
|
|
|
|
|
- round
|
|
|
|
|
- size="xs"
|
|
|
|
|
- :outline="group.visible"
|
|
|
|
|
- :unelevated="!group.visible"
|
|
|
|
|
- @click="group.visible = false"
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </template>
|
|
|
|
|
- </template>
|
|
|
|
|
- </div>
|
|
|
|
|
- </q-scroll-area>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <ProposalPricingTab
|
|
|
|
|
+ v-model="form.irrecusavel"
|
|
|
|
|
+ :product-options="productOptions"
|
|
|
|
|
+ show-condition
|
|
|
|
|
+ />
|
|
|
</div>
|
|
</div>
|
|
|
</q-card-section>
|
|
</q-card-section>
|
|
|
</q-scroll-area>
|
|
</q-scroll-area>
|
|
@@ -518,7 +242,7 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { computed, onMounted, ref, watch } from "vue";
|
|
|
|
|
|
|
+import { onMounted, computed, ref } from "vue";
|
|
|
|
|
|
|
|
import {
|
|
import {
|
|
|
createPackage,
|
|
createPackage,
|
|
@@ -526,10 +250,7 @@ import {
|
|
|
updatePackage,
|
|
updatePackage,
|
|
|
} from "src/api/package";
|
|
} from "src/api/package";
|
|
|
|
|
|
|
|
-import { formatUnitName } from "src/helpers/utils";
|
|
|
|
|
-import { getGroupsForSelect } from "src/api/group";
|
|
|
|
|
import { getProductsForSelect } from "src/api/product";
|
|
import { getProductsForSelect } from "src/api/product";
|
|
|
-import { getUnitsForSelect } from "src/api/unit";
|
|
|
|
|
import { useDialogPluginComponent } from "quasar";
|
|
import { useDialogPluginComponent } from "quasar";
|
|
|
import { useForm } from "src/composables/useForm";
|
|
import { useForm } from "src/composables/useForm";
|
|
|
import { useInputRules } from "src/composables/useInputRules";
|
|
import { useInputRules } from "src/composables/useInputRules";
|
|
@@ -542,6 +263,7 @@ import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue
|
|
|
import DefaultForm from "src/components/defaults/DefaultForm.vue";
|
|
import DefaultForm from "src/components/defaults/DefaultForm.vue";
|
|
|
import DefaultInput from "src/components/defaults/DefaultInput.vue";
|
|
import DefaultInput from "src/components/defaults/DefaultInput.vue";
|
|
|
import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
|
|
import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
|
|
|
|
|
+import ProposalPricingTab from "./ProposalPricingTab.vue";
|
|
|
|
|
|
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
|
|
|
|
|
@@ -562,12 +284,36 @@ const { scrollToComponent } = useScroll();
|
|
|
const formRef = ref(null);
|
|
const formRef = ref(null);
|
|
|
const scrollAreaRef = ref(null);
|
|
const scrollAreaRef = ref(null);
|
|
|
|
|
|
|
|
|
|
+const defaultPricingBlock = (withCondition = false) => ({
|
|
|
|
|
+ classes_discount_percentage: null,
|
|
|
|
|
+ classes_included_in_course: false,
|
|
|
|
|
+ classes_installments_allowed: false,
|
|
|
|
|
+ classes_max_installments: 13,
|
|
|
|
|
+ classes_value: null,
|
|
|
|
|
+
|
|
|
|
|
+ materials: [{ price: null, product_id: null, quantity: 1 }],
|
|
|
|
|
+ materials_included_in_course: false,
|
|
|
|
|
+ materials_installments_allowed: false,
|
|
|
|
|
+ materials_max_installments: 13,
|
|
|
|
|
+
|
|
|
|
|
+ registration_included_in_course: false,
|
|
|
|
|
+ registration_installments_allowed: false,
|
|
|
|
|
+ registration_max_installments: 13,
|
|
|
|
|
+ registration_value: null,
|
|
|
|
|
+
|
|
|
|
|
+ total_max_installments: 13,
|
|
|
|
|
+ total_value: null,
|
|
|
|
|
+
|
|
|
|
|
+ ...(withCondition ? { condition: null } : {}),
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
const { form, getUpdatedFields, setUpdateFormAsOriginal } = useForm(
|
|
const { form, getUpdatedFields, setUpdateFormAsOriginal } = useForm(
|
|
|
{
|
|
{
|
|
|
class_duration_hours: (props.package?.class_duration_minutes ?? 120) / 60,
|
|
class_duration_hours: (props.package?.class_duration_minutes ?? 120) / 60,
|
|
|
contract_register_value: props.package?.contract_register_value ?? null,
|
|
contract_register_value: props.package?.contract_register_value ?? null,
|
|
|
contract_value: props.package?.contract_value ?? null,
|
|
contract_value: props.package?.contract_value ?? null,
|
|
|
contrat_discount_value: props.package?.contrat_discount_value ?? null,
|
|
contrat_discount_value: props.package?.contrat_discount_value ?? null,
|
|
|
|
|
+ irrecusavel: defaultPricingBlock(true),
|
|
|
materials: [
|
|
materials: [
|
|
|
{
|
|
{
|
|
|
price: null,
|
|
price: null,
|
|
@@ -576,6 +322,7 @@ const { form, getUpdatedFields, setUpdateFormAsOriginal } = useForm(
|
|
|
},
|
|
},
|
|
|
],
|
|
],
|
|
|
name: props.package?.name ?? null,
|
|
name: props.package?.name ?? null,
|
|
|
|
|
+ pavao: defaultPricingBlock(),
|
|
|
quantity_classes: props.package?.quantity_classes ?? null,
|
|
quantity_classes: props.package?.quantity_classes ?? null,
|
|
|
},
|
|
},
|
|
|
{ containerRef: scrollAreaRef },
|
|
{ containerRef: scrollAreaRef },
|
|
@@ -592,48 +339,8 @@ const {
|
|
|
scrollFn: scrollToComponent,
|
|
scrollFn: scrollToComponent,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-const currentTab = ref("informacoes");
|
|
|
|
|
-const groupSearch = ref("");
|
|
|
|
|
-const groups = ref([]);
|
|
|
|
|
-const loadingGroups = ref(false);
|
|
|
|
|
-const loadingUnits = ref(false);
|
|
|
|
|
-const originalGroupIds = ref([]);
|
|
|
|
|
-const originalUnitVisibilities = ref([]);
|
|
|
|
|
|
|
+const currentTab = ref("dados");
|
|
|
const products = ref([]);
|
|
const products = ref([]);
|
|
|
-const unitSearch = ref("");
|
|
|
|
|
-const units = ref([]);
|
|
|
|
|
-
|
|
|
|
|
-const filteredGroups = computed(() => {
|
|
|
|
|
- const search = groupSearch.value.toLowerCase();
|
|
|
|
|
-
|
|
|
|
|
- if (!search) return groups.value;
|
|
|
|
|
-
|
|
|
|
|
- return groups.value.filter((group) =>
|
|
|
|
|
- group.name.toLowerCase().includes(search),
|
|
|
|
|
- );
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const filteredUnits = computed(() => {
|
|
|
|
|
- const search = unitSearch.value.toLowerCase();
|
|
|
|
|
-
|
|
|
|
|
- if (!search) return units.value;
|
|
|
|
|
-
|
|
|
|
|
- return units.value.filter((unit) =>
|
|
|
|
|
- formatUnitName(unit).toLowerCase().includes(search),
|
|
|
|
|
- );
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const lockedUnitIds = computed(() => {
|
|
|
|
|
- const ids = new Set();
|
|
|
|
|
-
|
|
|
|
|
- groups.value.forEach((group) => {
|
|
|
|
|
- if (group.visible && group.unit_ids) {
|
|
|
|
|
- group.unit_ids.forEach((id) => ids.add(id));
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- return ids;
|
|
|
|
|
-});
|
|
|
|
|
|
|
|
|
|
const productOptions = computed(() =>
|
|
const productOptions = computed(() =>
|
|
|
products.value.map((product) => ({
|
|
products.value.map((product) => ({
|
|
@@ -644,9 +351,9 @@ const productOptions = computed(() =>
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
const tabs = [
|
|
const tabs = [
|
|
|
- { label: "Informações", name: "informacoes" },
|
|
|
|
|
- { label: "Grupos", name: "grupos" },
|
|
|
|
|
- { label: "Unidades", name: "unidades" },
|
|
|
|
|
|
|
+ { label: "Dados", name: "dados" },
|
|
|
|
|
+ { label: "Pavão", name: "pavao" },
|
|
|
|
|
+ { label: "Irrecusável", name: "irrecusavel" },
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
const addMaterial = () => {
|
|
const addMaterial = () => {
|
|
@@ -671,99 +378,97 @@ const removeMaterial = (index) => {
|
|
|
form.materials.splice(index, 1);
|
|
form.materials.splice(index, 1);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const setAllGroupsVisible = (visible) => {
|
|
|
|
|
- groups.value.forEach((group) => {
|
|
|
|
|
- group.visible = visible;
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const setAllVisible = (visible) => {
|
|
|
|
|
- units.value.forEach((unit) => {
|
|
|
|
|
- if (!visible && lockedUnitIds.value.has(unit.id)) return;
|
|
|
|
|
-
|
|
|
|
|
- unit.visible = visible;
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
|
|
+const mapPricingBlockFromApi = (data, existingMaterials) => ({
|
|
|
|
|
+ classes_discount_percentage: data.classes_discount_percentage,
|
|
|
|
|
+ classes_included_in_course: data.classes_included_in_course,
|
|
|
|
|
+ classes_installments_allowed: data.classes_installments_allowed,
|
|
|
|
|
+ classes_max_installments: data.classes_max_installments,
|
|
|
|
|
+ classes_value: data.classes_value,
|
|
|
|
|
+
|
|
|
|
|
+ materials: data.materials?.length
|
|
|
|
|
+ ? data.materials.map((material) => ({
|
|
|
|
|
+ price: material.price,
|
|
|
|
|
+ product_id: material.product_id,
|
|
|
|
|
+ quantity: material.quantity,
|
|
|
|
|
+ }))
|
|
|
|
|
+ : existingMaterials,
|
|
|
|
|
+ materials_included_in_course: data.materials_included_in_course,
|
|
|
|
|
+ materials_installments_allowed: data.materials_installments_allowed,
|
|
|
|
|
+ materials_max_installments: data.materials_max_installments,
|
|
|
|
|
+
|
|
|
|
|
+ registration_included_in_course: data.registration_included_in_course,
|
|
|
|
|
+ registration_installments_allowed: data.registration_installments_allowed,
|
|
|
|
|
+ registration_max_installments: data.registration_max_installments,
|
|
|
|
|
+ registration_value: data.registration_value,
|
|
|
|
|
+
|
|
|
|
|
+ total_max_installments: data.total_max_installments,
|
|
|
|
|
+ total_value: data.total_value,
|
|
|
|
|
+
|
|
|
|
|
+ ...(data.condition !== undefined ? { condition: data.condition } : {}),
|
|
|
|
|
+});
|
|
|
|
|
|
|
|
-const loadData = async () => {
|
|
|
|
|
- loadingGroups.value = true;
|
|
|
|
|
- loadingUnits.value = true;
|
|
|
|
|
|
|
+const numOrNull = (value) =>
|
|
|
|
|
+ value === null || value === "" || value === undefined ? null : Number(value);
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- const [allUnits, allProducts, allGroups] = await Promise.all([
|
|
|
|
|
- getUnitsForSelect(),
|
|
|
|
|
- getProductsForSelect(),
|
|
|
|
|
- getGroupsForSelect(),
|
|
|
|
|
- ]);
|
|
|
|
|
|
|
+const toPricingPayload = (block, { withCondition = false } = {}) => {
|
|
|
|
|
+ const payload = {
|
|
|
|
|
+ classes_discount_percentage: numOrNull(block.classes_discount_percentage),
|
|
|
|
|
+ classes_included_in_course: block.classes_included_in_course,
|
|
|
|
|
+ classes_installments_allowed: block.classes_installments_allowed,
|
|
|
|
|
+ classes_max_installments: Number(block.classes_max_installments) || 13,
|
|
|
|
|
+ classes_value: numOrNull(block.classes_value),
|
|
|
|
|
+
|
|
|
|
|
+ materials: block.materials
|
|
|
|
|
+ .filter((material) => material.product_id)
|
|
|
|
|
+ .map((material) => ({
|
|
|
|
|
+ price: Number(material.price),
|
|
|
|
|
+ product_id: material.product_id,
|
|
|
|
|
+ quantity: Number(material.quantity),
|
|
|
|
|
+ })),
|
|
|
|
|
+ materials_included_in_course: block.materials_included_in_course,
|
|
|
|
|
+ materials_installments_allowed: block.materials_installments_allowed,
|
|
|
|
|
+ materials_max_installments: Number(block.materials_max_installments) || 13,
|
|
|
|
|
+
|
|
|
|
|
+ registration_included_in_course: block.registration_included_in_course,
|
|
|
|
|
+ registration_installments_allowed: block.registration_installments_allowed,
|
|
|
|
|
+ registration_max_installments: Number(block.registration_max_installments) || 13,
|
|
|
|
|
+ registration_value: numOrNull(block.registration_value),
|
|
|
|
|
+
|
|
|
|
|
+ total_max_installments: Number(block.total_max_installments) || 13,
|
|
|
|
|
+ total_value: numOrNull(block.total_value),
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- products.value = allProducts;
|
|
|
|
|
|
|
+ if (withCondition) {
|
|
|
|
|
+ payload.condition = block.condition ?? null;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const visibilityMap = {};
|
|
|
|
|
|
|
+ return payload;
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
- let savedMaterials = null;
|
|
|
|
|
- let savedGroupIds = [];
|
|
|
|
|
|
|
+const loadData = async () => {
|
|
|
|
|
+ products.value = await getProductsForSelect();
|
|
|
|
|
|
|
|
- if (props.package?.id) {
|
|
|
|
|
- const packageData = await getPackage(props.package.id);
|
|
|
|
|
-
|
|
|
|
|
- (packageData.unit_visibilities ?? []).forEach((visibility) => {
|
|
|
|
|
- visibilityMap[visibility.unit_id] = visibility.visible;
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- if (packageData.materials?.length) {
|
|
|
|
|
- savedMaterials = packageData.materials.map((material) => ({
|
|
|
|
|
- price: material.price,
|
|
|
|
|
- product_id: material.product_id,
|
|
|
|
|
- quantity: material.quantity,
|
|
|
|
|
- }));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (props.package?.id) {
|
|
|
|
|
+ const packageData = await getPackage(props.package.id);
|
|
|
|
|
|
|
|
- savedGroupIds = packageData.group_ids ?? [];
|
|
|
|
|
|
|
+ if (packageData.materials?.length) {
|
|
|
|
|
+ form.materials = packageData.materials.map((material) => ({
|
|
|
|
|
+ price: material.price,
|
|
|
|
|
+ product_id: material.product_id,
|
|
|
|
|
+ quantity: material.quantity,
|
|
|
|
|
+ }));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- groups.value = allGroups.map((group) => ({
|
|
|
|
|
- id: group.id,
|
|
|
|
|
- name: group.name,
|
|
|
|
|
- unit_ids: group.unit_ids ?? [],
|
|
|
|
|
- visible: savedGroupIds.includes(group.id),
|
|
|
|
|
- }));
|
|
|
|
|
-
|
|
|
|
|
- const groupLockedIds = new Set();
|
|
|
|
|
-
|
|
|
|
|
- groups.value.forEach((group) => {
|
|
|
|
|
- if (group.visible) {
|
|
|
|
|
- group.unit_ids.forEach((id) => groupLockedIds.add(id));
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- units.value = allUnits.map((unit) => ({
|
|
|
|
|
- fantasy_name: unit.fantasy_name,
|
|
|
|
|
- id: unit.id,
|
|
|
|
|
- name: unit.name,
|
|
|
|
|
- social_reason: unit.social_reason,
|
|
|
|
|
- visible: groupLockedIds.has(unit.id)
|
|
|
|
|
- ? true
|
|
|
|
|
- : (visibilityMap[unit.id] ?? true),
|
|
|
|
|
- }));
|
|
|
|
|
-
|
|
|
|
|
- if (savedMaterials) {
|
|
|
|
|
- form.materials = savedMaterials;
|
|
|
|
|
|
|
+ if (packageData.pavao) {
|
|
|
|
|
+ form.pavao = mapPricingBlockFromApi(packageData.pavao, form.pavao.materials);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- originalGroupIds.value = groups.value
|
|
|
|
|
- .filter((group) => group.visible)
|
|
|
|
|
- .map((group) => group.id);
|
|
|
|
|
-
|
|
|
|
|
- originalUnitVisibilities.value = units.value.map((unit) => ({
|
|
|
|
|
- unit_id: unit.id,
|
|
|
|
|
- visible: unit.visible,
|
|
|
|
|
- }));
|
|
|
|
|
-
|
|
|
|
|
- setUpdateFormAsOriginal();
|
|
|
|
|
- } finally {
|
|
|
|
|
- loadingGroups.value = false;
|
|
|
|
|
- loadingUnits.value = false;
|
|
|
|
|
|
|
+ if (packageData.irrecusavel) {
|
|
|
|
|
+ form.irrecusavel = mapPricingBlockFromApi(packageData.irrecusavel, form.irrecusavel.materials);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ setUpdateFormAsOriginal();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const onOKClick = async () => {
|
|
const onOKClick = async () => {
|
|
@@ -780,9 +485,7 @@ const onOKClick = async () => {
|
|
|
contract_value: form.contract_value,
|
|
contract_value: form.contract_value,
|
|
|
contrat_discount_value: form.contrat_discount_value,
|
|
contrat_discount_value: form.contrat_discount_value,
|
|
|
|
|
|
|
|
- group_ids: groups.value
|
|
|
|
|
- .filter((group) => group.visible)
|
|
|
|
|
- .map((group) => group.id),
|
|
|
|
|
|
|
+ irrecusavel: toPricingPayload(form.irrecusavel, { withCondition: true }),
|
|
|
|
|
|
|
|
materials: validMaterials.map((material) => ({
|
|
materials: validMaterials.map((material) => ({
|
|
|
price: Number(material.price),
|
|
price: Number(material.price),
|
|
@@ -791,12 +494,8 @@ const onOKClick = async () => {
|
|
|
})),
|
|
})),
|
|
|
|
|
|
|
|
name: form.name,
|
|
name: form.name,
|
|
|
|
|
+ pavao: toPricingPayload(form.pavao),
|
|
|
quantity_classes: form.quantity_classes,
|
|
quantity_classes: form.quantity_classes,
|
|
|
-
|
|
|
|
|
- unit_visibilities: units.value.map((unit) => ({
|
|
|
|
|
- unit_id: unit.id,
|
|
|
|
|
- visible: unit.visible,
|
|
|
|
|
- })),
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
await execute(() => {
|
|
await execute(() => {
|
|
@@ -806,32 +505,15 @@ const onOKClick = async () => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
if (updated.class_duration_hours !== undefined) {
|
|
if (updated.class_duration_hours !== undefined) {
|
|
|
- updated.class_duration_minutes = Math.round(
|
|
|
|
|
- Number(updated.class_duration_hours) * 60,
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ updated.class_duration_minutes = payload.class_duration_minutes;
|
|
|
|
|
|
|
|
delete updated.class_duration_hours;
|
|
delete updated.class_duration_hours;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const groupIds = groups.value
|
|
|
|
|
- .filter((group) => group.visible)
|
|
|
|
|
- .map((group) => group.id);
|
|
|
|
|
-
|
|
|
|
|
- const unitVisibilities = units.value.map((unit) => ({
|
|
|
|
|
- unit_id: unit.id,
|
|
|
|
|
- visible: unit.visible,
|
|
|
|
|
- }));
|
|
|
|
|
-
|
|
|
|
|
- if (JSON.stringify(groupIds) !== JSON.stringify(originalGroupIds.value)) {
|
|
|
|
|
- updated.group_ids = groupIds;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (
|
|
|
|
|
- JSON.stringify(unitVisibilities) !==
|
|
|
|
|
- JSON.stringify(originalUnitVisibilities.value)
|
|
|
|
|
- ) {
|
|
|
|
|
- updated.unit_visibilities = unitVisibilities;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Pavão/Irrecusável têm validação cruzada entre campos (par exclusivo
|
|
|
|
|
+ // de checkbox) que não é seguro fatiar num diff parcial — vão inteiros.
|
|
|
|
|
+ updated.pavao = payload.pavao;
|
|
|
|
|
+ updated.irrecusavel = payload.irrecusavel;
|
|
|
|
|
|
|
|
return updatePackage(props.package.id, updated);
|
|
return updatePackage(props.package.id, updated);
|
|
|
}
|
|
}
|
|
@@ -840,18 +522,6 @@ const onOKClick = async () => {
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-watch(
|
|
|
|
|
- lockedUnitIds,
|
|
|
|
|
- (newLockedIds) => {
|
|
|
|
|
- units.value.forEach((unit) => {
|
|
|
|
|
- if (newLockedIds.has(unit.id)) {
|
|
|
|
|
- unit.visible = true;
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- },
|
|
|
|
|
- { deep: true },
|
|
|
|
|
-);
|
|
|
|
|
-
|
|
|
|
|
onMounted(loadData);
|
|
onMounted(loadData);
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|