| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129 |
- <template>
- <q-dialog ref="dialogRef" @hide="onDialogHide">
- <q-card class="q-dialog-plugin dialog-form-card" :style="dialogCardStyle">
- <DefaultDialogHeader
- :title="props.contract ? 'Editar Contrato' : 'Novo Contrato'"
- @close="onDialogCancel"
- />
- <DefaultForm ref="formRef" @submit="handleSave">
- <q-scroll-area ref="scrollAreaRef" class="dialog-form-scroll">
- <q-card-section class="q-pt-sm">
- <div>
- <template v-if="!props.contract">
- <DefaultInput
- :model-value="store.user?.name"
- class="q-mb-md"
- disable
- label="Franqueado operador"
- />
- <div class="text-subtitle1 q-mb-md">Dados da Unidade</div>
- <div class="row q-col-gutter-sm">
- <DefaultInput
- v-model:error="validationErrors.unit_id"
- :model-value="unitDetails.name"
- class="col-12 col-md-6"
- disable
- label="Nome da Unidade"
- />
- <DefaultInput
- v-model:error="validationErrors.unit_id"
- :model-value="unitDetails.cnpj"
- :mask="masks.Brasil.cnpj"
- class="col-12 col-md-6"
- disable
- label="CNPJ"
- />
- </div>
- <div class="text-subtitle1 q-mt-lg q-mb-md">Dados do Aluno</div>
- <div class="row q-col-gutter-sm">
- <div v-if="props.selectStudent" class="col-12">
- <div
- class="row items-start no-wrap q-col-gutter-sm"
- @keydown.enter.prevent
- >
- <DefaultSelect
- v-model="selectedStudent"
- v-model:error="validationErrors.student_id"
- class="col"
- dropdown-icon="mdi-magnify"
- fill-input
- hide-selected
- input-debounce="0"
- label="Aluno"
- option-label="name"
- use-input
- :options="filteredStudents"
- :rules="[inputRules.required]"
- @filter="filterStudents"
- >
- <template #no-option>
- <q-item>
- <q-item-section class="text-grey">
- {{ $t("http.errors.no_records_found") }}
- </q-item-section>
- </q-item>
- </template>
- </DefaultSelect>
- <div v-if="canAddStudents" class="col-auto">
- <q-btn
- aria-label="Cadastrar novo aluno"
- color="primary"
- icon="mdi-plus"
- round
- unelevated
- @click="openCreateStudent"
- >
- <q-tooltip> Cadastrar novo aluno </q-tooltip>
- </q-btn>
- </div>
- </div>
- </div>
- <div v-if="!props.selectStudent" class="col-12">
- <DefaultInput
- v-model:error="validationErrors.student_id"
- :model-value="currentStudent?.name"
- :rules="[inputRules.required]"
- disable
- label="Aluno"
- />
- </div>
- <div class="col-12">
- <DefaultInput
- v-model:error="validationErrors.birth_date"
- :model-value="formattedBirthDate"
- disable
- label="Data de Nascimento"
- />
- </div>
- </div>
- </template>
- <div class="row items-center justify-between q-mt-lg q-mb-md">
- <div class="text-subtitle1">
- Dados do Contrato
- </div>
- <q-toggle
- v-model="isAutomaticProtocol"
- color="primary"
- label="Protocolo automático"
- left-label
- dense
- @update:model-value="handleToggleAutomaticProtocol"
- />
- </div>
- <div class="row q-col-gutter-sm">
- <div class="col-4">
- <DefaultInput
- v-model="form.protocol"
- v-model:error="validationErrors.protocol"
- :disable="isAutomaticProtocol"
- :loading="loadingProtocol"
- label="Protocolo"
- :rules="[inputRules.max(255)]"
- />
- </div>
- <div class="col-4">
- <DefaultInputDatePicker
- v-model="form.signature_date"
- v-model:error="validationErrors.signature_date"
- :disable="!!props.contract"
- :rules="[inputRules.date]"
- label="Data de Início"
- />
- </div>
- <div class="col-4">
- <DefaultInputDatePicker
- v-model="form.end_date"
- v-model:error="validationErrors.end_date"
- label="Data Encerramento"
- reactive-rules
- :lazy-rules="false"
- :rules="[
- inputRules.date,
- props.contract
- ? inputRules.renewalEndDate(
- () => props.contract?.end_date,
- )
- : inputRules.dateMaxYearsFrom(
- () => form.signature_date,
- ),
- ]"
- />
- </div>
- <div class="col-4">
- <DefaultSelect
- v-model="form.package_id"
- v-model:error="validationErrors.class_package_unit_id"
- emit-value
- label="Pacote de Aulas"
- map-options
- option-label="name"
- option-value="id"
- :options="packages"
- :rules="props.contract ? [] : [inputRules.required]"
- />
- </div>
- <div class="col-3">
- <DefaultSelect
- v-model="form.pricing_mode"
- v-model:error="validationErrors.pricing_mode"
- emit-value
- label="Modalidade"
- map-options
- option-label="label"
- option-value="value"
- :options="modalityOptions"
- :rules="
- modalityOptions.length
- ? [inputRules.required]
- : []
- "
- />
- </div>
- <div class="col-5">
- <DefaultInput
- v-model="form.class_quantity"
- v-model:error="validationErrors.class_quantity"
- disable
- label="Qtd. Aulas"
- type="number"
- :rules="[inputRules.integerRange(1)]"
- />
- </div>
- <div class="col-6">
- <DefaultCurrencyInput
- :model-value="form.total_value"
- disable
- label="Total do Contrato"
- />
- </div>
- <div class="col-6">
- <DefaultInput
- :model-value="contractWeeks"
- disable
- label="Total de Semanas"
- />
- </div>
- <div class="col-4">
- <DefaultSelect
- v-model="form.weekday"
- v-model:error="validationErrors.weekday"
- emit-value
- label="Dia da Semana"
- map-options
- option-label="label"
- option-value="value"
- :options="weekdays"
- :rules="[inputRules.integerRange(0, 6)]"
- />
- </div>
- <div class="col-4">
- <DefaultInput
- v-model="form.start_time"
- v-model:error="validationErrors.start_time"
- label="Hora de Início"
- mask="##:##"
- :rules="[inputRules.time]"
- >
- <template #append>
- <q-icon name="mdi-clock-outline" />
- </template>
- </DefaultInput>
- </div>
- <div class="col-4">
- <DefaultInput
- v-model="form.end_time"
- v-model:error="validationErrors.end_time"
- label="Hora de Término"
- mask="##:##"
- :rules="[inputRules.time]"
- >
- <template #append>
- <q-icon name="mdi-clock-outline" />
- </template>
- </DefaultInput>
- </div>
- <div class="col-4">
- <DefaultSelect
- v-model="form.second_weekday"
- v-model:error="validationErrors.second_weekday"
- emit-value
- label="2° Dia da Semana"
- map-options
- option-label="label"
- option-value="value"
- :options="weekdays"
- :rules="[inputRules.integerRange(0, 6)]"
- />
- </div>
- <div class="col-4">
- <DefaultInput
- v-model="form.second_start_time"
- v-model:error="validationErrors.second_start_time"
- label="Hora de Início"
- mask="##:##"
- :rules="[inputRules.time]"
- >
- <template #append>
- <q-icon name="mdi-clock-outline" />
- </template>
- </DefaultInput>
- </div>
- <div class="col-4">
- <DefaultInput
- v-model="form.second_end_time"
- v-model:error="validationErrors.second_end_time"
- label="Hora de Término"
- mask="##:##"
- :rules="[inputRules.time]"
- >
- <template #append>
- <q-icon name="mdi-clock-outline" />
- </template>
- </DefaultInput>
- </div>
- </div>
- <div class="text-subtitle1 q-mt-lg q-mb-md">
- Dados Financeiros
- </div>
- <div class="row q-col-gutter-sm">
- <div class="col-4">
- <DefaultInput
- v-model="form.due_day"
- v-model:error="validationErrors.due_day"
- label="Dia de Vencimento"
- type="number"
- :rules="[inputRules.integerRange(1, 31)]"
- />
- </div>
- </div>
- <div class="text-subtitle2 q-mt-md q-mb-sm text-grey-8">
- Total do Curso
- </div>
- <div class="row q-col-gutter-sm">
- <div class="col-3">
- <DefaultCurrencyInput
- v-model="form.total_value"
- v-model:error="validationErrors.total_value"
- label="Valor Total do Curso"
- :disable="!props.contract"
- />
- </div>
- <div class="col-3">
- <DefaultSelect
- v-model="form.total_installments"
- v-model:error="validationErrors.total_installments"
- emit-value
- label="Qtde Parcelas"
- map-options
- option-label="label"
- option-value="value"
- :options="packageInstallmentOptions"
- :rules="[inputRules.integerRange(1, 13)]"
- />
- </div>
- <div class="col-3">
- <DefaultCurrencyInput
- v-model:error="validationErrors.total_installments"
- :model-value="totalInstallmentValue"
- disable
- label="Valor da Parcela"
- />
- </div>
- <div class="col-3">
- <DefaultInputDatePicker
- v-model="form.total_due_date"
- v-model:error="validationErrors.total_due_date"
- label="Data 1ª Parcela"
- :rules="[inputRules.date]"
- />
- </div>
- </div>
- <div class="row q-col-gutter-sm q-mt-xs">
- <div class="col-6">
- <DefaultSelect
- v-model="form.payment_method"
- v-model:error="validationErrors.payment_method"
- emit-value
- label="Forma de Pagamento"
- map-options
- option-label="label"
- option-value="value"
- :options="paymentMethods"
- :rules="[
- inputRules.oneOf(['pix', 'credit_card', 'debit_card']),
- ]"
- />
- </div>
- <div class="col-6">
- <DefaultInput
- v-model="form.late_fee"
- v-model:error="validationErrors.fine_cancelled"
- label="Multa (%)"
- type="number"
- :rules="[inputRules.minValue(0), inputRules.maxValue(100)]"
- />
- </div>
- </div>
- </div>
- </q-card-section>
- </q-scroll-area>
- <q-card-actions align="right" class="q-px-md q-pb-md">
- <q-btn
- color="primary"
- label="Cancelar"
- no-caps
- outline
- @click="onDialogCancel"
- />
- <q-btn
- color="primary"
- label="Salvar"
- no-caps
- type="submit"
- :loading="saving"
- />
- </q-card-actions>
- </DefaultForm>
- </q-card>
- </q-dialog>
- </template>
- <script setup>
- import {
- addYears,
- differenceInCalendarDays,
- format,
- isValid,
- parse,
- } from "date-fns";
- import {
- createStudentContract,
- getNextContractProtocol,
- updateStudentContract,
- } from "src/api/studentContract";
- import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from "vue";
- import { formatDateDMYtoYMD, formatDateYMDtoDMY } from "src/helpers/utils";
- import { getStudentsForSelect } from "src/api/student";
- import { getUnitMe, updateUnitMe } from "src/api/unit";
- import { getUnitPackagesForSelect } from "src/api/package";
- import { permissionStore } from "src/stores/permission";
- import { useDialogPluginComponent, useQuasar } from "quasar";
- import { useForm } from "src/composables/useForm";
- import { useInputRules } from "src/composables/useInputRules";
- import { useScroll } from "src/composables/useScroll";
- import { useSubmitHandler } from "src/composables/useSubmitHandler";
- import { userStore } from "src/stores/user";
- import masks from "src/helpers/masks";
- import DefaultCurrencyInput from "src/components/defaults/DefaultCurrencyInput.vue";
- import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
- import DefaultForm from "src/components/defaults/DefaultForm.vue";
- import DefaultInput from "src/components/defaults/DefaultInput.vue";
- import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePicker.vue";
- import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
- import AddEditStudentDialog from "src/pages/students/components/AddEditStudentDialog.vue";
- defineEmits([...useDialogPluginComponent.emits]);
- const props = defineProps({
- contract: {
- type: Object,
- default: null,
- },
- selectStudent: {
- type: Boolean,
- default: false,
- },
- student: {
- type: Object,
- default: null,
- },
- });
- const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
- useDialogPluginComponent();
- const $q = useQuasar();
- const { inputRules } = useInputRules();
- const { scrollToComponent } = useScroll();
- const permissions = permissionStore();
- const store = userStore();
- const formRef = useTemplateRef("formRef");
- const scrollAreaRef = useTemplateRef("scrollAreaRef");
- const dateConfirmationOpen = ref(false);
- const dateOverrideConfirmed = ref(false);
- const filteredStudents = ref([]);
- const isAutomaticProtocol = ref(true);
- const loadingProtocol = ref(false);
- const packages = ref([]);
- const restoringDefaultDates = ref(false);
- const selectedStudent = ref(props.student);
- const students = ref([]);
- const studentSearchTerm = ref("");
- const syncingFromDate = ref(false);
- const syncingFromDay = ref(false);
- const unitDetails = ref(store.selectedUnit ?? {});
- const canAddStudents = computed(() =>
- permissions.getAccess("franchisee_students", "add"),
- );
- const currentStudent = computed(() => selectedStudent.value ?? props.student);
- const contractWeeks = computed(() => {
- if (!form.signature_date || !form.end_date) return null;
- const startDate = parse(form.signature_date, "dd/MM/yyyy", new Date());
- const endDate = parse(form.end_date, "dd/MM/yyyy", new Date());
- if (!isValid(startDate) || !isValid(endDate)) {
- return null;
- }
- const days = differenceInCalendarDays(endDate, startDate);
- if (days < 0) return null;
- return Math.max(1, Math.round(days / 7));
- });
- const dialogCardStyle = computed(() => ({
- height: props.selectStudent ? "95vh" : "auto",
- maxHeight: props.selectStudent ? "95vh" : "none",
- maxWidth: props.selectStudent ? "1400px" : "1350px",
- width: "100%",
- }));
- const formattedBirthDate = computed(() =>
- currentStudent.value?.birth_date
- ? formatDateYMDtoDMY(currentStudent.value.birth_date)
- : "",
- );
- const totalInstallmentValue = computed(() => {
- if (!form.total_value || !form.total_installments) {
- return null;
- }
- return Number((form.total_value / form.total_installments).toFixed(2));
- });
- const modalityOptions = computed(() => {
- const selectedPackage = packages.value.find(
- (item) => item.id === form.package_id,
- );
- if (!selectedPackage) return [];
- const options = [];
- if (selectedPackage.pavao) options.push({ label: "Pavão", value: "pavao" });
- if (selectedPackage.irrecusavel) {
- options.push({ label: "Irrecusável", value: "irrecusavel" });
- }
- return options;
- });
- const completeDatePattern = /^\d{2}\/\d{2}\/\d{4}$/;
- const defaultContractDates = (() => {
- if (props.contract) return null;
- const startDate = new Date();
- return {
- end_date: format(addYears(startDate, 1), "dd/MM/yyyy"),
- signature_date: format(startDate, "dd/MM/yyyy"),
- };
- })();
- const packageInstallmentOptions = Array.from({ length: 13 }, (_, index) => ({
- label: `${index + 1}x`,
- value: index + 1,
- }));
- const paymentMethods = [
- {
- label: "Pix",
- value: "pix",
- },
- {
- label: "Cartão de Crédito",
- value: "credit_card",
- },
- {
- label: "Cartão de Débito",
- value: "debit_card",
- },
- ];
- const weekdays = [
- {
- label: "Segunda",
- value: 1,
- },
- {
- label: "Terça",
- value: 2,
- },
- {
- label: "Quarta",
- value: 3,
- },
- {
- label: "Quinta",
- value: 4,
- },
- {
- label: "Sexta",
- value: 5,
- },
- {
- label: "Sábado",
- value: 6,
- },
- {
- label: "Domingo",
- value: 0,
- },
- ];
- const trimTime = (time) => (time ? time.slice(0, 5) : null);
- const { form, getUpdatedFields } = useForm({
- class_quantity: props.contract?.class_quantity ?? null,
- due_day: props.contract?.due_day ?? null,
- end_date: props.contract?.end_date ?? defaultContractDates?.end_date ?? null,
- end_time: trimTime(props.contract?.end_time),
- late_fee: props.contract?.fine_cancelled ?? null,
- package_id: props.contract?.class_package_unit_id ?? null,
- payment_method: props.contract?.payment_method ?? null,
- pricing_mode: props.contract?.pricing_mode ?? null,
- protocol: props.contract?.protocol ?? null,
- total_due_date: props.contract?.total_due_date ?? null,
- total_installments: props.contract?.total_installments ?? null,
- total_value: props.contract?.total_value ?? null,
- second_end_time: trimTime(props.contract?.second_end_time),
- second_start_time: trimTime(props.contract?.second_start_time),
- second_weekday: props.contract?.second_weekday ?? null,
- signature_date:
- props.contract?.signature_date ??
- defaultContractDates?.signature_date ??
- null,
- start_time: trimTime(props.contract?.start_time),
- total_classes: props.contract?.class_quantity ?? null,
- weekday: props.contract?.weekday ?? null,
- });
- const {
- loading: saving,
- validationErrors,
- execute,
- } = useSubmitHandler({
- containerRef: scrollAreaRef,
- formRef,
- onSuccess: () => {
- onDialogOK(true);
- },
- scrollFn: scrollToComponent,
- });
- const buildDateFromDay = (day) => {
- const parsedDay = Number.parseInt(day);
- if (!parsedDay || parsedDay < 1 || parsedDay > 31) {
- return null;
- }
- const now = new Date();
- const nextMonthIndex = (now.getMonth() + 1) % 12;
- const nextYear =
- now.getMonth() === 11 ? now.getFullYear() + 1 : now.getFullYear();
- const lastDayOfNextMonth = new Date(
- nextYear,
- nextMonthIndex + 1,
- 0,
- ).getDate();
- const safeDay = Math.min(parsedDay, lastDayOfNextMonth);
- return `${String(safeDay).padStart(2, "0")}/${String(
- nextMonthIndex + 1,
- ).padStart(2, "0")}/${nextYear}`;
- };
- const buildPayload = () => ({
- class_package_unit_id: form.package_id,
- class_quantity: form.class_quantity,
- due_day: form.due_day ? Number.parseInt(form.due_day) : null,
- end_date: form.end_date ? formatDateDMYtoYMD(form.end_date) : null,
- end_time: form.end_time,
- fine_cancelled: form.late_fee,
- payment_method: form.payment_method,
- pricing_mode: form.pricing_mode,
- protocol: form.protocol,
- second_end_time: form.second_end_time,
- second_start_time: form.second_start_time,
- second_weekday: form.second_weekday,
- signature_date: form.signature_date
- ? formatDateDMYtoYMD(form.signature_date)
- : null,
- start_time: form.start_time,
- student_id: currentStudent.value?.id ?? null,
- total_due_date: form.total_due_date
- ? formatDateDMYtoYMD(form.total_due_date)
- : null,
- total_installments: form.total_installments,
- total_value: form.total_value,
- weekday: form.weekday,
- });
- const calculateEndTime = (startTime, durationMinutes = 120) => {
- if (!/^\d{2}:\d{2}$/.test(startTime ?? "")) {
- return null;
- }
- const [hours, minutes] = startTime.split(":").map(Number);
- if (hours > 23 || minutes > 59) {
- return null;
- }
- const endMinutes = (hours * 60 + minutes + durationMinutes) % (24 * 60);
- return `${String(Math.floor(endMinutes / 60)).padStart(2, "0")}:${String(
- endMinutes % 60,
- ).padStart(2, "0")}`;
- };
- const filterStudents = (value, update) => {
- studentSearchTerm.value = value.trim();
- update(() => {
- const search = value.trim().toLocaleLowerCase("pt-BR");
- filteredStudents.value = search
- ? students.value.filter((student) =>
- student.name.toLocaleLowerCase("pt-BR").includes(search),
- )
- : students.value;
- });
- };
- const handleSave = async () => {
- const payload = buildPayload();
- await execute(() => {
- if (!props.contract) {
- return createStudentContract(payload);
- }
- const changedFields = getUpdatedFields.value;
- const updatePayload = {};
- const fieldMap = {
- late_fee: "fine_cancelled",
- package_id: "class_package_unit_id",
- total_classes: "class_quantity",
- };
- for (const key of [
- "protocol",
- "class_quantity",
- "weekday",
- "start_time",
- "end_time",
- "second_weekday",
- "second_start_time",
- "second_end_time",
- "payment_method",
- "pricing_mode",
- "total_value",
- "total_installments",
- ]) {
- if (key in changedFields) {
- updatePayload[key] = payload[key];
- }
- }
- for (const [source, target] of Object.entries(fieldMap)) {
- if (source in changedFields) {
- updatePayload[target] = payload[target];
- }
- }
- for (const key of [
- "signature_date",
- "end_date",
- "total_due_date",
- ]) {
- if (key in changedFields) {
- updatePayload[key] = payload[key];
- }
- }
- if ("due_day" in changedFields) {
- updatePayload.due_day = payload.due_day;
- }
- return updateStudentContract(props.contract.id, updatePayload);
- });
- };
- const openCreateStudent = () => {
- $q.dialog({
- component: AddEditStudentDialog,
- componentProps: {
- initialName: studentSearchTerm.value || null,
- },
- }).onOk(async (createdStudent) => {
- const unitStudents = await getStudentsForSelect();
- students.value = unitStudents;
- filteredStudents.value = unitStudents;
- selectedStudent.value =
- unitStudents.find((student) => student.id === createdStudent?.id) ??
- createdStudent;
- });
- };
- const restoreDefaultContractDates = () => {
- restoringDefaultDates.value = true;
- form.signature_date = defaultContractDates.signature_date;
- form.end_date = defaultContractDates.end_date;
- nextTick(() => {
- dateConfirmationOpen.value = false;
- restoringDefaultDates.value = false;
- });
- };
- watch(
- [() => form.signature_date, () => form.end_date],
- ([signatureDate, endDate]) => {
- if (
- props.contract ||
- dateOverrideConfirmed.value ||
- dateConfirmationOpen.value ||
- restoringDefaultDates.value
- ) {
- return;
- }
- const datesAreStillDefault =
- signatureDate === defaultContractDates.signature_date &&
- endDate === defaultContractDates.end_date;
- if (datesAreStillDefault) return;
- const datesAreComplete =
- (!signatureDate || completeDatePattern.test(signatureDate)) &&
- (!endDate || completeDatePattern.test(endDate));
- if (!datesAreComplete) return;
- dateConfirmationOpen.value = true;
- $q.dialog({
- cancel: {
- color: "primary",
- label: "Não",
- outline: true,
- },
- message:
- "As datas foram preenchidas automaticamente para a duração de um ano. Tem certeza de que deseja alterá-las?",
- ok: {
- color: "primary",
- label: "Sim",
- },
- persistent: true,
- title: "Alterar duração do contrato?",
- })
- .onOk(() => {
- dateConfirmationOpen.value = false;
- dateOverrideConfirmed.value = true;
- })
- .onCancel(restoreDefaultContractDates);
- },
- );
- watch(
- () => form.due_day,
- (day) => {
- if (syncingFromDate.value) return;
- const dateString = buildDateFromDay(day);
- if (!dateString) return;
- syncingFromDay.value = true;
- form.package_due_date = dateString;
- nextTick(() => {
- syncingFromDay.value = false;
- });
- },
- );
- watch(
- () => form.package_due_date,
- (dateString) => {
- if (syncingFromDay.value) return;
- if (!dateString || dateString.length < 8) return;
- const day = Number.parseInt(dateString.split("/")[0]);
- if (!Number.isNaN(day) && day !== Number.parseInt(form.due_day)) {
- syncingFromDate.value = true;
- form.due_day = day;
- nextTick(() => {
- syncingFromDate.value = false;
- });
- }
- },
- );
- watch(
- () => form.package_id,
- (id) => {
- const selectedPackage = packages.value.find((item) => item.id === id);
- if (!selectedPackage) return;
- form.class_quantity = selectedPackage.quantity_classes;
- form.total_classes = selectedPackage.quantity_classes;
- form.weekday = selectedPackage.weekday ?? null;
- form.start_time = trimTime(selectedPackage.start_time);
- form.end_time = calculateEndTime(
- form.start_time,
- selectedPackage.class_duration_minutes ?? 120,
- );
- form.second_weekday = selectedPackage.second_weekday ?? null;
- form.second_start_time = trimTime(selectedPackage.second_start_time);
- form.second_end_time = calculateEndTime(
- form.second_start_time,
- selectedPackage.class_duration_minutes ?? 120,
- );
- // Escolhe automaticamente a modalidade quando só há uma disponível;
- // se a proposta ainda não tem Pavão nem Irrecusável, limpa a escolha.
- if (selectedPackage.pavao) {
- form.pricing_mode = "pavao";
- } else if (selectedPackage.irrecusavel) {
- form.pricing_mode = "irrecusavel";
- } else {
- form.pricing_mode = null;
- }
- },
- );
- // Os valores do contrato vêm da modalidade escolhida na proposta comercial:
- // o que estiver marcado "incluso no valor do curso" não é cobrado à parte
- // (só entra no Total do Curso); o que "permite parcelar" vira cobrança
- // separada com o próprio valor/parcelas daquele item.
- watch(
- [() => form.package_id, () => form.pricing_mode],
- ([id, mode]) => {
- const selectedPackage = packages.value.find((item) => item.id === id);
- const modality =
- mode === "irrecusavel"
- ? selectedPackage?.irrecusavel
- : selectedPackage?.pavao;
- if (!modality) {
- form.total_value = null;
- form.total_installments = null;
- return;
- }
- form.total_value = modality.total_value;
- form.total_installments = modality.total_max_installments;
- },
- );
- watch(
- () => form.start_time,
- (startTime) => {
- const selectedPackage = packages.value.find(
- (item) => item.id === form.package_id,
- );
- const calculatedEndTime = calculateEndTime(
- startTime,
- selectedPackage?.class_duration_minutes ?? 120,
- );
- if (calculatedEndTime) {
- form.end_time = calculatedEndTime;
- }
- },
- );
- watch(
- () => form.second_start_time,
- (startTime) => {
- const selectedPackage = packages.value.find(
- (item) => item.id === form.package_id,
- );
- const calculatedEndTime = calculateEndTime(
- startTime,
- selectedPackage?.class_duration_minutes ?? 120,
- );
- if (calculatedEndTime) {
- form.second_end_time = calculatedEndTime;
- }
- },
- );
- const handleToggleAutomaticProtocol = async (value) => {
- try {
- await updateUnitMe({ automatic_protocol: value });
- if (unitDetails.value) {
- unitDetails.value.automatic_protocol = value;
- }
- if (store.selectedUnit) {
- store.selectedUnit.automatic_protocol = value;
- }
- if (value && !props.contract) {
- loadingProtocol.value = true;
- try {
- const next = await getNextContractProtocol();
- form.protocol = next?.protocol ?? "000001";
- } finally {
- loadingProtocol.value = false;
- }
- }
- } catch (error) {
- console.error("Falha ao salvar configuração de protocolo automático:", error);
- $q.notify({
- message: "Não foi possível atualizar a preferência de protocolo da unidade.",
- type: "negative",
- });
- }
- };
- onMounted(async () => {
- const unitRequest = getUnitMe().catch((error) => {
- console.error("Falha ao carregar os dados da unidade:", error);
- return store.selectedUnit;
- });
- const requests = [getUnitPackagesForSelect(), unitRequest];
- if (props.selectStudent) {
- requests.push(getStudentsForSelect());
- }
- const [unitPackages, unitData, unitStudents = []] =
- await Promise.all(requests);
- packages.value = unitPackages;
- unitDetails.value = unitData ?? store.selectedUnit ?? {};
- isAutomaticProtocol.value = unitDetails.value?.automatic_protocol ?? true;
- if (!props.contract && isAutomaticProtocol.value) {
- loadingProtocol.value = true;
- try {
- const next = await getNextContractProtocol();
- form.protocol = next?.protocol ?? "000001";
- } catch (error) {
- console.error("Falha ao carregar próximo protocolo:", error);
- form.protocol = "000001";
- } finally {
- loadingProtocol.value = false;
- }
- }
- students.value = unitStudents;
- filteredStudents.value = unitStudents;
- });
- </script>
|