| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335 |
- <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"
- />
- <template v-if="props.contract">
- <q-tabs
- v-model="activeTab"
- active-color="primary"
- align="left"
- class="q-px-md text-grey-7"
- dense
- indicator-color="primary"
- >
- <q-tab
- label="Dados do Contrato"
- name="dados"
- />
- <q-tab
- label="Mídias do Contrato"
- name="midias"
- />
- </q-tabs>
- <q-separator />
- </template>
- <DefaultForm
- ref="formRef"
- @submit="handleSave"
- >
- <q-scroll-area
- ref="scrollAreaRef"
- class="dialog-form-scroll"
- >
- <q-card-section class="q-pt-sm">
- <div v-show="activeTab === 'dados'">
- <DefaultInput
- :model-value="store.user?.name"
- class="q-mb-md"
- label="Franqueado operador"
- readonly
- />
- <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"
- label="Nome da Unidade"
- readonly
- />
- <DefaultInput
- v-model:error="validationErrors.unit_id"
- :model-value="unitDetails.cnpj"
- :mask="masks.Brasil.cnpj"
- class="col-12 col-md-6"
- label="CNPJ"
- readonly
- />
- </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"
- />
- <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>
- <div class="text-subtitle1 q-mt-lg q-mb-md">
- Dados do Contrato
- </div>
- <div class="row q-col-gutter-sm">
- <div class="col-4">
- <DefaultInput
- v-model="form.protocol"
- v-model:error="validationErrors.protocol"
- label="Protocolo"
- :rules="[inputRules.max(255)]"
- />
- </div>
- <div class="col-4">
- <DefaultInputDatePicker
- v-model="form.signature_date"
- v-model:error="validationErrors.signature_date"
- label="Data Assinatura"
- :rules="[inputRules.date]"
- />
- </div>
- <div class="col-4">
- <DefaultInputDatePicker
- v-model="form.end_date"
- v-model:error="validationErrors.end_date"
- label="Data Encerramento"
- :rules="[inputRules.date]"
- />
- </div>
- <div class="col-5">
- <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="[inputRules.required]"
- />
- </div>
- <div class="col-7">
- <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-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">
- Matrícula
- </div>
- <div class="row q-col-gutter-sm">
- <div class="col-3">
- <DefaultCurrencyInput
- v-model="form.enrollment_fee"
- v-model:error="validationErrors.tax_register"
- disable
- label="Valor da Matrícula"
- />
- </div>
- <div class="col-3">
- <DefaultSelect
- v-model="form.installments"
- v-model:error="validationErrors.installments"
- emit-value
- label="Qtde Parcelas"
- map-options
- option-label="label"
- option-value="value"
- :options="enrollmentInstallmentOptions"
- :rules="[inputRules.integerRange(1, 12)]"
- />
- </div>
- <div class="col-3">
- <DefaultCurrencyInput
- v-model:error="validationErrors.installments"
- :model-value="enrollmentInstallmentValue"
- disable
- label="Valor da Parcela"
- />
- </div>
- <div class="col-3">
- <DefaultInputDatePicker
- v-model="form.enrollment_due_date"
- v-model:error="validationErrors.enrollment_due_date"
- label="Data Vencimento"
- :rules="[inputRules.date]"
- />
- </div>
- </div>
- <div class="text-subtitle2 q-mt-md q-mb-sm text-grey-8">
- Pacote
- </div>
- <div class="row q-col-gutter-sm">
- <div class="col-3">
- <DefaultCurrencyInput
- v-model="form.package_value"
- v-model:error="validationErrors.package_value"
- disable
- label="Valor do Pacote"
- />
- </div>
- <div class="col-3">
- <DefaultSelect
- v-model="form.package_installments"
- v-model:error="validationErrors.package_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.package_installments"
- :model-value="packageInstallmentValue"
- disable
- label="Valor da Parcela"
- />
- </div>
- <div class="col-3">
- <DefaultInputDatePicker
- v-model="form.package_due_date"
- v-model:error="validationErrors.package_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">
- <DefaultInput
- v-model="form.early_payment_discount"
- v-model:error="validationErrors.early_payment_discount"
- label="Desconto até o vencimento (%)"
- type="number"
- :rules="[
- inputRules.minValue(0),
- inputRules.maxValue(100),
- ]"
- />
- </div>
- <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.interest_rate"
- v-model:error="validationErrors.interest_rate"
- label="Juros (%) a.m"
- type="number"
- :rules="[inputRules.minValue(0)]"
- />
- </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>
- <div
- v-if="props.contract"
- v-show="activeTab === 'midias'"
- >
- <DefaultTable
- v-model:rows="medias"
- :columns="mediaColumns"
- :feminino="true"
- :loading="loadingMedias"
- :show-search-field="false"
- descricao="mídias"
- no-api-call
- title="Mídias"
- >
- <template #body-cell-actions="{ row }">
- <q-td align="center">
- <q-item-section
- class="no-wrap"
- style="flex-direction: row; gap: 4px"
- >
- <q-btn
- icon="mdi-eye-outline"
- outline
- style="width: 36px"
- @click.prevent.stop="openFile(row.file_url)"
- />
- <q-btn
- color="negative"
- icon="mdi-trash-can-outline"
- outline
- style="width: 36px"
- @click.prevent.stop="handleDeleteMedia(row)"
- />
- </q-item-section>
- </q-td>
- </template>
- </DefaultTable>
- </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
- v-if="activeTab === 'dados'"
- color="primary"
- label="Salvar"
- no-caps
- type="submit"
- :loading="saving"
- />
- </q-card-actions>
- </DefaultForm>
- </q-card>
- </q-dialog>
- </template>
- <script setup>
- import { addYears, format } from "date-fns";
- import {
- createStudentContract,
- updateStudentContract,
- } from "src/api/studentContract";
- import {
- computed,
- nextTick,
- onMounted,
- ref,
- useTemplateRef,
- watch,
- } from "vue";
- import {
- deleteContractMedia,
- getContractMedias,
- } from "src/api/student_media";
- import {
- formatDateDMYtoYMD,
- formatDateYMDtoDMY,
- } from "src/helpers/utils";
- import { getFinancialMe } from "src/api/unit_financial";
- import { getStudentsForSelect } from "src/api/student";
- import { getUnitMe } 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 DefaultTable from "src/components/defaults/DefaultTable.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 activeTab = ref("dados");
- const dateConfirmationOpen = ref(false);
- const dateOverrideConfirmed = ref(false);
- const filteredStudents = ref([]);
- const loadingMedias = ref(false);
- const medias = ref([]);
- const packages = ref([]);
- const restoringDefaultDates = ref(false);
- const selectedStudent = ref(props.student);
- const students = ref([]);
- const syncingFromDate = ref(false);
- const syncingFromDay = ref(false);
- const unitDetails = ref(store.selectedUnit ?? {});
- const canAddStudents = computed(() =>
- permissions.getAccess("franchisee_students", "add"),
- );
- const canViewFinancial = computed(() =>
- permissions.getAccess("franchisee_financial", "view"),
- );
- const currentStudent = computed(
- () => selectedStudent.value ?? props.student,
- );
- const dialogCardStyle = computed(() => ({
- height: props.selectStudent ? "95vh" : "auto",
- maxHeight: props.selectStudent ? "95vh" : "none",
- maxWidth: props.selectStudent ? "1400px" : "1350px",
- width: "100%",
- }));
- const enrollmentInstallmentValue = computed(() => {
- if (!form.enrollment_fee || !form.installments) {
- return null;
- }
- return Number(
- (form.enrollment_fee / form.installments).toFixed(2),
- );
- });
- const formattedBirthDate = computed(() =>
- currentStudent.value?.birth_date
- ? formatDateYMDtoDMY(currentStudent.value.birth_date)
- : "",
- );
- const packageInstallmentValue = computed(() => {
- if (!form.package_value || !form.package_installments) {
- return null;
- }
- return Number(
- (
- form.package_value /
- form.package_installments
- ).toFixed(2),
- );
- });
- 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 enrollmentInstallmentOptions = Array.from(
- { length: 12 },
- (_, index) => ({
- label: `${index + 1}x`,
- value: index + 1,
- }),
- );
- const mediaColumns = [
- {
- align: "left",
- field: "created_at",
- label: "Data de Anexo",
- name: "created_at",
- },
- {
- align: "center",
- field: null,
- label: "Ações",
- name: "actions",
- },
- ];
- 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?.recurring_day ?? null,
- early_payment_discount:
- props.contract?.early_payment_discount ?? null,
- end_date:
- props.contract?.end_date ??
- defaultContractDates?.end_date ??
- null,
- end_time: trimTime(props.contract?.end_time),
- enrollment_due_date:
- props.contract?.enrollment_due_date ?? null,
- enrollment_fee: props.contract?.tax_register ?? null,
- installments: props.contract?.installments ?? null,
- interest_rate: props.contract?.interest_rate ?? null,
- late_fee: props.contract?.fine_cancelled ?? null,
- package_due_date: props.contract?.package_due_date ?? null,
- package_id:
- props.contract?.class_package_unit_id ?? null,
- package_installments:
- props.contract?.package_installments ?? null,
- package_value: props.contract?.package_value ?? null,
- payment_method: props.contract?.payment_method ?? null,
- protocol: props.contract?.protocol ?? 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,
- early_payment_discount: form.early_payment_discount,
- end_date: form.end_date
- ? formatDateDMYtoYMD(form.end_date)
- : null,
- end_time: form.end_time,
- enrollment_due_date: form.enrollment_due_date
- ? formatDateDMYtoYMD(form.enrollment_due_date)
- : null,
- fine_cancelled: form.late_fee,
- installments: form.installments,
- interest_rate: form.interest_rate,
- package_due_date: form.package_due_date
- ? formatDateDMYtoYMD(form.package_due_date)
- : null,
- package_installments: form.package_installments,
- package_value: form.package_value,
- payment_method: form.payment_method,
- 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,
- tax_register: form.enrollment_fee,
- 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 fetchMedias = async () => {
- if (!props.contract) return;
- loadingMedias.value = true;
- try {
- medias.value = await getContractMedias(
- props.contract.id,
- );
- } finally {
- loadingMedias.value = false;
- }
- };
- const filterStudents = (value, update) => {
- 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 handleDeleteMedia = (media) => {
- $q.dialog({
- cancel: {
- color: "primary",
- label: "Cancelar",
- outline: true,
- },
- message: "Deseja excluir esta mídia permanentemente?",
- ok: {
- color: "negative",
- label: "Excluir",
- },
- title: "Excluir mídia",
- }).onOk(async () => {
- try {
- await deleteContractMedia(media.id);
- medias.value = medias.value.filter(
- (item) => item.id !== media.id,
- );
- } catch (error) {
- console.error(error);
- $q.notify({
- message: "Erro ao excluir mídia.",
- type: "negative",
- });
- }
- });
- };
- const handleSave = async () => {
- const payload = buildPayload();
- await execute(() => {
- if (!props.contract) {
- return createStudentContract(payload);
- }
- const changedFields = getUpdatedFields.value;
- const updatePayload = {
- student_id: currentStudent.value?.id ?? null,
- };
- const fieldMap = {
- enrollment_fee: "tax_register",
- 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",
- "installments",
- "package_value",
- "package_installments",
- "early_payment_discount",
- "interest_rate",
- "payment_method",
- ]) {
- 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",
- "enrollment_due_date",
- "package_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,
- }).onOk(async (createdStudent) => {
- const unitStudents = await getStudentsForSelect();
- students.value = unitStudents;
- filteredStudents.value = unitStudents;
- selectedStudent.value =
- unitStudents.find(
- (student) => student.id === createdStudent?.id,
- ) ?? createdStudent;
- });
- };
- const openFile = (url) => {
- window.open(url, "_blank");
- };
- 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(activeTab, (tab) => {
- if (tab === "midias") {
- fetchMedias();
- }
- });
- 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.enrollment_fee =
- selectedPackage.contract_register_value;
- form.package_value =
- selectedPackage.contract_value;
- 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,
- );
- },
- );
- 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;
- }
- },
- );
- 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 ?? {};
- students.value = unitStudents;
- filteredStudents.value = unitStudents;
- if (!props.contract && canViewFinancial.value) {
- try {
- const financial = await getFinancialMe();
- if (!financial) return;
- if (financial.default_discount != null) {
- form.early_payment_discount =
- financial.default_discount;
- }
- if (financial.default_interest != null) {
- form.interest_rate =
- financial.default_interest;
- }
- if (financial.default_fine != null) {
- form.late_fee = financial.default_fine;
- }
- } catch (error) {
- console.error(error);
- }
- }
- });
- </script>
|