|
@@ -1,6 +1,6 @@
|
|
|
<template>
|
|
<template>
|
|
|
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
|
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
|
|
- <div style="width: 100%; max-width: 700px">
|
|
|
|
|
|
|
+ <div style="width: 100%; max-width: 960px">
|
|
|
<q-card
|
|
<q-card
|
|
|
class="dialog-form-card"
|
|
class="dialog-form-card"
|
|
|
style="width: 100%"
|
|
style="width: 100%"
|
|
@@ -68,104 +68,6 @@
|
|
|
A duração padrão das aulas é de 2 horas e pode ser alterada se
|
|
A duração padrão das aulas é de 2 horas e pode ser alterada se
|
|
|
necessário. É possível configurar no máximo dois dias.
|
|
necessário. É possível configurar no máximo dois dias.
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
- <DefaultCurrencyInput
|
|
|
|
|
- v-model="form.contract_register_value"
|
|
|
|
|
- v-model:error="validationErrors.contract_register_value"
|
|
|
|
|
- class="col-4"
|
|
|
|
|
- label="R$ Matrícula"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <DefaultCurrencyInput
|
|
|
|
|
- v-model="form.contract_value"
|
|
|
|
|
- v-model:error="validationErrors.contract_value"
|
|
|
|
|
- class="col-4"
|
|
|
|
|
- label="R$ Total do Contrato"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <DefaultInput
|
|
|
|
|
- v-model="form.contrat_discount_value"
|
|
|
|
|
- class="col-4"
|
|
|
|
|
- label="Desconto em %"
|
|
|
|
|
- max="100"
|
|
|
|
|
- min="0"
|
|
|
|
|
- type="number"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <div
|
|
|
|
|
- v-for="(material, index) in form.materials"
|
|
|
|
|
- :key="index"
|
|
|
|
|
- class="col-12"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="row q-col-gutter-sm items-center">
|
|
|
|
|
- <DefaultSelect
|
|
|
|
|
- v-model="material.product_id"
|
|
|
|
|
- class="col-4"
|
|
|
|
|
- emit-value
|
|
|
|
|
- label="Material"
|
|
|
|
|
- map-options
|
|
|
|
|
- :options="productOptions"
|
|
|
|
|
- :rules="
|
|
|
|
|
- material.product_id
|
|
|
|
|
- ? [inputRules.required]
|
|
|
|
|
- : []
|
|
|
|
|
- "
|
|
|
|
|
- @update:model-value="onProductSelected(material)"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <DefaultInput
|
|
|
|
|
- v-model="material.quantity"
|
|
|
|
|
- class="col-4"
|
|
|
|
|
- label="Qtd"
|
|
|
|
|
- min="1"
|
|
|
|
|
- type="number"
|
|
|
|
|
- :rules="
|
|
|
|
|
- material.product_id
|
|
|
|
|
- ? [
|
|
|
|
|
- inputRules.required,
|
|
|
|
|
- inputRules.minValue(1),
|
|
|
|
|
- ]
|
|
|
|
|
- : []
|
|
|
|
|
- "
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <DefaultCurrencyInput
|
|
|
|
|
- v-model="material.price"
|
|
|
|
|
- class="col"
|
|
|
|
|
- label="R$ Unitário"
|
|
|
|
|
- :rules="
|
|
|
|
|
- material.product_id
|
|
|
|
|
- ? [inputRules.required]
|
|
|
|
|
- : []
|
|
|
|
|
- "
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <div class="col-auto">
|
|
|
|
|
- <q-btn
|
|
|
|
|
- v-if="index === form.materials.length - 1"
|
|
|
|
|
- color="primary"
|
|
|
|
|
- icon="mdi-plus"
|
|
|
|
|
- style="
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- height: 40px;
|
|
|
|
|
- width: 40px;
|
|
|
|
|
- "
|
|
|
|
|
- unelevated
|
|
|
|
|
- @click="addMaterial"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <q-btn
|
|
|
|
|
- v-else
|
|
|
|
|
- color="negative"
|
|
|
|
|
- dense
|
|
|
|
|
- flat
|
|
|
|
|
- icon="mdi-delete-outline"
|
|
|
|
|
- round
|
|
|
|
|
- @click="removeMaterial(index)"
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -175,7 +77,9 @@
|
|
|
>
|
|
>
|
|
|
<ProposalPricingTab
|
|
<ProposalPricingTab
|
|
|
v-model="form.pavao"
|
|
v-model="form.pavao"
|
|
|
|
|
+ v-model:enabled="form.pavao_enabled"
|
|
|
:product-options="productOptions"
|
|
:product-options="productOptions"
|
|
|
|
|
+ section-label="Pavão"
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -185,7 +89,9 @@
|
|
|
>
|
|
>
|
|
|
<ProposalPricingTab
|
|
<ProposalPricingTab
|
|
|
v-model="form.irrecusavel"
|
|
v-model="form.irrecusavel"
|
|
|
|
|
+ v-model:enabled="form.irrecusavel_enabled"
|
|
|
:product-options="productOptions"
|
|
:product-options="productOptions"
|
|
|
|
|
+ section-label="Irrecusável"
|
|
|
show-condition
|
|
show-condition
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
@@ -241,11 +147,9 @@ import { useScroll } from "src/composables/useScroll";
|
|
|
import { useSubmitHandler } from "src/composables/useSubmitHandler";
|
|
import { useSubmitHandler } from "src/composables/useSubmitHandler";
|
|
|
|
|
|
|
|
import CustomTabComponent from "src/components/shared/CustomTabComponent.vue";
|
|
import CustomTabComponent from "src/components/shared/CustomTabComponent.vue";
|
|
|
-import DefaultCurrencyInput from "src/components/defaults/DefaultCurrencyInput.vue";
|
|
|
|
|
import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
|
|
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 ProposalPricingTab from "./ProposalPricingTab.vue";
|
|
import ProposalPricingTab from "./ProposalPricingTab.vue";
|
|
|
|
|
|
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
@@ -311,26 +215,12 @@ const { form, getUpdatedFields } = useForm({
|
|
|
class_duration_hours:
|
|
class_duration_hours:
|
|
|
(props.package?.class_duration_minutes ?? 120) / 60,
|
|
(props.package?.class_duration_minutes ?? 120) / 60,
|
|
|
|
|
|
|
|
- contract_register_value:
|
|
|
|
|
- props.package?.contract_register_value ?? null,
|
|
|
|
|
-
|
|
|
|
|
- contract_value: props.package?.contract_value ?? null,
|
|
|
|
|
-
|
|
|
|
|
- contrat_discount_value:
|
|
|
|
|
- props.package?.contrat_discount_value ?? null,
|
|
|
|
|
-
|
|
|
|
|
irrecusavel: defaultPricingBlock(true),
|
|
irrecusavel: defaultPricingBlock(true),
|
|
|
-
|
|
|
|
|
- materials: [
|
|
|
|
|
- {
|
|
|
|
|
- price: null,
|
|
|
|
|
- product_id: null,
|
|
|
|
|
- quantity: 1,
|
|
|
|
|
- },
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+ irrecusavel_enabled: false,
|
|
|
|
|
|
|
|
name: props.package?.name ?? null,
|
|
name: props.package?.name ?? null,
|
|
|
pavao: defaultPricingBlock(),
|
|
pavao: defaultPricingBlock(),
|
|
|
|
|
+ pavao_enabled: false,
|
|
|
quantity_classes: props.package?.quantity_classes ?? null,
|
|
quantity_classes: props.package?.quantity_classes ?? null,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -347,14 +237,6 @@ const {
|
|
|
scrollFn: scrollToComponent,
|
|
scrollFn: scrollToComponent,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-const addMaterial = () => {
|
|
|
|
|
- form.materials.push({
|
|
|
|
|
- price: null,
|
|
|
|
|
- product_id: null,
|
|
|
|
|
- quantity: 1,
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
const mapPricingBlockFromApi = (data, existingMaterials) => ({
|
|
const mapPricingBlockFromApi = (data, existingMaterials) => ({
|
|
|
classes_discount_percentage: data.classes_discount_percentage,
|
|
classes_discount_percentage: data.classes_discount_percentage,
|
|
|
classes_included_in_course: data.classes_included_in_course,
|
|
classes_included_in_course: data.classes_included_in_course,
|
|
@@ -423,33 +305,17 @@ const toPricingPayload = (block, { withCondition = false } = {}) => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const onOKClick = async () => {
|
|
const onOKClick = async () => {
|
|
|
- const validMaterials = form.materials.filter(
|
|
|
|
|
- (material) => material.product_id,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
const payload = {
|
|
const payload = {
|
|
|
class_duration_minutes: Math.round(
|
|
class_duration_minutes: Math.round(
|
|
|
Number(form.class_duration_hours) * 60,
|
|
Number(form.class_duration_hours) * 60,
|
|
|
),
|
|
),
|
|
|
|
|
|
|
|
- contract_register_value:
|
|
|
|
|
- form.contract_register_value,
|
|
|
|
|
-
|
|
|
|
|
- contract_value: form.contract_value,
|
|
|
|
|
-
|
|
|
|
|
- contrat_discount_value:
|
|
|
|
|
- form.contrat_discount_value,
|
|
|
|
|
-
|
|
|
|
|
- irrecusavel: toPricingPayload(form.irrecusavel, { withCondition: true }),
|
|
|
|
|
-
|
|
|
|
|
- materials: validMaterials.map((material) => ({
|
|
|
|
|
- price: Number(material.price),
|
|
|
|
|
- product_id: material.product_id,
|
|
|
|
|
- quantity: Number(material.quantity),
|
|
|
|
|
- })),
|
|
|
|
|
|
|
+ irrecusavel: form.irrecusavel_enabled
|
|
|
|
|
+ ? toPricingPayload(form.irrecusavel, { withCondition: true })
|
|
|
|
|
+ : null,
|
|
|
|
|
|
|
|
name: form.name,
|
|
name: form.name,
|
|
|
- pavao: toPricingPayload(form.pavao),
|
|
|
|
|
|
|
+ pavao: form.pavao_enabled ? toPricingPayload(form.pavao) : null,
|
|
|
quantity_classes: form.quantity_classes,
|
|
quantity_classes: form.quantity_classes,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -461,13 +327,7 @@ const onOKClick = async () => {
|
|
|
const changedFields = getUpdatedFields.value;
|
|
const changedFields = getUpdatedFields.value;
|
|
|
const updatePayload = {};
|
|
const updatePayload = {};
|
|
|
|
|
|
|
|
- const directKeys = [
|
|
|
|
|
- "name",
|
|
|
|
|
- "quantity_classes",
|
|
|
|
|
- "contract_value",
|
|
|
|
|
- "contract_register_value",
|
|
|
|
|
- "contrat_discount_value",
|
|
|
|
|
- ];
|
|
|
|
|
|
|
+ const directKeys = ["name", "quantity_classes"];
|
|
|
|
|
|
|
|
for (const key of directKeys) {
|
|
for (const key of directKeys) {
|
|
|
if (key in changedFields) {
|
|
if (key in changedFields) {
|
|
@@ -480,12 +340,8 @@ const onOKClick = async () => {
|
|
|
payload.class_duration_minutes;
|
|
payload.class_duration_minutes;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if ("materials" in changedFields) {
|
|
|
|
|
- updatePayload.materials = payload.materials;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
// Pavão/Irrecusável têm validação cruzada entre campos (par exclusivo de
|
|
// 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.
|
|
|
|
|
|
|
+ // checkbox) e um sinal explícito de "removido" (null) — vão sempre inteiros.
|
|
|
updatePayload.pavao = payload.pavao;
|
|
updatePayload.pavao = payload.pavao;
|
|
|
updatePayload.irrecusavel = payload.irrecusavel;
|
|
updatePayload.irrecusavel = payload.irrecusavel;
|
|
|
|
|
|
|
@@ -496,20 +352,6 @@ const onOKClick = async () => {
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const onProductSelected = (material) => {
|
|
|
|
|
- const option = productOptions.value.find(
|
|
|
|
|
- (product) => product.value === material.product_id,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- if (option) {
|
|
|
|
|
- material.price = option.price_sale;
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const removeMaterial = (index) => {
|
|
|
|
|
- form.materials.splice(index, 1);
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
|
const productsResponse = await getProductsForSelect();
|
|
const productsResponse = await getProductsForSelect();
|
|
|
|
|
|
|
@@ -524,15 +366,8 @@ onMounted(async () => {
|
|
|
form.class_duration_hours =
|
|
form.class_duration_hours =
|
|
|
(packageData.class_duration_minutes ?? 120) / 60;
|
|
(packageData.class_duration_minutes ?? 120) / 60;
|
|
|
|
|
|
|
|
- if (packageData.materials?.length) {
|
|
|
|
|
- form.materials = packageData.materials.map(
|
|
|
|
|
- (material) => ({
|
|
|
|
|
- price: material.price,
|
|
|
|
|
- product_id: material.product_id,
|
|
|
|
|
- quantity: material.quantity,
|
|
|
|
|
- }),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ form.pavao_enabled = !!packageData.pavao;
|
|
|
|
|
+ form.irrecusavel_enabled = !!packageData.irrecusavel;
|
|
|
|
|
|
|
|
if (packageData.pavao) {
|
|
if (packageData.pavao) {
|
|
|
form.pavao = mapPricingBlockFromApi(packageData.pavao, form.pavao.materials);
|
|
form.pavao = mapPricingBlockFromApi(packageData.pavao, form.pavao.materials);
|