CreateContractDialog.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. <template>
  2. <q-dialog ref="dialogRef" @hide="onDialogHide">
  3. <q-card
  4. class="q-dialog-plugin overflow-hidden"
  5. style="width: 100%; max-width: 1100px"
  6. >
  7. <DefaultDialogHeader :title="props.contract ? 'Editar Contrato' : 'Novo Contrato'" @close="onDialogCancel" />
  8. <q-card-section class="q-pt-sm" style="height: 65vh; overflow-y: auto">
  9. <div class="text-subtitle1 q-mb-md">Dados do Aluno</div>
  10. <div class="row q-col-gutter-sm">
  11. <div class="col-12">
  12. <DefaultInput
  13. :model-value="props.student.name"
  14. label="Aluno"
  15. disable
  16. />
  17. </div>
  18. <div class="col-6">
  19. <DefaultInput
  20. :model-value="props.student.document_number"
  21. label="CPF"
  22. disable
  23. />
  24. </div>
  25. <div class="col-6">
  26. <DefaultInput
  27. :model-value="formattedBirthDate"
  28. label="Data de Nascimento"
  29. disable
  30. />
  31. </div>
  32. </div>
  33. <div class="text-subtitle1 q-mt-lg q-mb-md">Dados do Contrato</div>
  34. <div class="row q-col-gutter-sm">
  35. <div class="col-4">
  36. <DefaultInput v-model="form.protocol" label="Protocolo" />
  37. </div>
  38. <div class="col-4">
  39. <DefaultInputDatePicker
  40. v-model="form.signature_date"
  41. label="Data Assinatura"
  42. />
  43. </div>
  44. <div class="col-4">
  45. <DefaultInputDatePicker
  46. v-model="form.end_date"
  47. label="Data Encerramento"
  48. />
  49. </div>
  50. <div class="col-5">
  51. <DefaultSelect
  52. v-model="form.package_id"
  53. label="Pacote de Aulas"
  54. :options="packages"
  55. option-value="id"
  56. option-label="name"
  57. emit-value
  58. map-options
  59. />
  60. </div>
  61. <div class="col-7">
  62. <DefaultInput
  63. v-model="form.class_quantity"
  64. label="Qtd. Aulas"
  65. type="number"
  66. disable
  67. />
  68. </div>
  69. <div class="col-4">
  70. <DefaultSelect
  71. v-model="form.weekday"
  72. label="Dia da Semana"
  73. :options="weekdays"
  74. option-value="value"
  75. option-label="label"
  76. emit-value
  77. map-options
  78. />
  79. </div>
  80. <div class="col-4">
  81. <DefaultInput
  82. v-model="form.start_time"
  83. label="Hora de Início"
  84. mask="##:##"
  85. >
  86. <template #append>
  87. <q-icon name="mdi-clock-outline" />
  88. </template>
  89. </DefaultInput>
  90. </div>
  91. <div class="col-4">
  92. <DefaultInput
  93. v-model="form.end_time"
  94. label="Hora de Término"
  95. mask="##:##"
  96. >
  97. <template #append>
  98. <q-icon name="mdi-clock-outline" />
  99. </template>
  100. </DefaultInput>
  101. </div>
  102. <div class="col-4">
  103. <DefaultSelect
  104. v-model="form.second_weekday"
  105. label="2° Dia da Semana"
  106. :options="weekdays"
  107. option-value="value"
  108. option-label="label"
  109. emit-value
  110. map-options
  111. />
  112. </div>
  113. <div class="col-4">
  114. <DefaultInput
  115. v-model="form.second_start_time"
  116. label="Hora de Início"
  117. mask="##:##"
  118. >
  119. <template #append>
  120. <q-icon name="mdi-clock-outline" />
  121. </template>
  122. </DefaultInput>
  123. </div>
  124. <div class="col-4">
  125. <DefaultInput
  126. v-model="form.second_end_time"
  127. label="Hora de Término"
  128. mask="##:##"
  129. >
  130. <template #append>
  131. <q-icon name="mdi-clock-outline" />
  132. </template>
  133. </DefaultInput>
  134. </div>
  135. </div>
  136. <div class="text-subtitle1 q-mt-lg q-mb-md">Dados Financeiros</div>
  137. <div class="row q-col-gutter-sm">
  138. <div class="col-4">
  139. <DefaultInputDatePicker
  140. v-model="form.due_date"
  141. label="Vencimento"
  142. />
  143. </div>
  144. <div class="col-4">
  145. <DefaultCurrencyInput
  146. v-model="form.enrollment_fee"
  147. label="Taxa de Matrícula"
  148. disable
  149. />
  150. </div>
  151. <div class="col-4">
  152. <DefaultInput
  153. v-model="form.total_classes"
  154. label="Total de Aulas"
  155. type="number"
  156. disable
  157. />
  158. </div>
  159. <div class="col-3">
  160. <DefaultCurrencyInput
  161. v-model="form.down_payment"
  162. label="Entrada"
  163. />
  164. </div>
  165. <div class="col-3">
  166. <DefaultSelect
  167. v-model="form.installments"
  168. label="Parcelas"
  169. :options="installmentOptions"
  170. option-value="value"
  171. option-label="label"
  172. emit-value
  173. map-options
  174. />
  175. </div>
  176. <div class="col-6">
  177. <DefaultInput
  178. v-model="form.early_payment_discount"
  179. label="Desconto até o vencimento (%)"
  180. type="number"
  181. />
  182. </div>
  183. <div class="col-3">
  184. <DefaultCurrencyInput
  185. v-model="form.material_value"
  186. label="Valor dos Materiais"
  187. />
  188. </div>
  189. <div class="col-3">
  190. <DefaultSelect
  191. v-model="form.material_installments"
  192. label="Parcelas"
  193. :options="installmentOptions"
  194. option-value="value"
  195. option-label="label"
  196. emit-value
  197. map-options
  198. />
  199. </div>
  200. <div class="col-6">
  201. <DefaultInput
  202. v-model="form.interest_rate"
  203. label="Juros (%) a.m"
  204. type="number"
  205. />
  206. </div>
  207. <div class="col-6">
  208. <DefaultSelect
  209. v-model="form.payment_method"
  210. label="Forma de Pagamento"
  211. :options="paymentMethods"
  212. option-value="value"
  213. option-label="label"
  214. emit-value
  215. map-options
  216. />
  217. </div>
  218. <div class="col-6">
  219. <DefaultInput
  220. v-model="form.late_fee"
  221. label="Multa (%)"
  222. type="number"
  223. />
  224. </div>
  225. </div>
  226. </q-card-section>
  227. <q-separator />
  228. <q-card-actions align="right">
  229. <q-btn
  230. outline
  231. color="primary"
  232. label="CANCELAR"
  233. @click="onDialogCancel"
  234. />
  235. <q-btn
  236. color="primary"
  237. label="SALVAR"
  238. :loading="saving"
  239. @click="handleSave"
  240. />
  241. </q-card-actions>
  242. </q-card>
  243. </q-dialog>
  244. </template>
  245. <script setup>
  246. import { computed, ref, watch, onMounted } from "vue";
  247. import { useDialogPluginComponent } from "quasar";
  248. import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
  249. import DefaultInput from "src/components/defaults/DefaultInput.vue";
  250. import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
  251. import DefaultCurrencyInput from "src/components/defaults/DefaultCurrencyInput.vue";
  252. import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePicker.vue";
  253. import { useSubmitHandler } from "src/composables/useSubmitHandler";
  254. import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
  255. import { formatDateYMDtoDMY, formatDateDMYtoYMD } from "src/helpers/utils";
  256. import { getUnitPackages } from "src/api/package";
  257. import { createStudentContract, updateStudentContract } from "src/api/studentContract";
  258. const props = defineProps({
  259. student: {
  260. type: Object,
  261. required: true,
  262. },
  263. contract: {
  264. type: Object,
  265. default: null,
  266. },
  267. });
  268. defineEmits([...useDialogPluginComponent.emits]);
  269. const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
  270. useDialogPluginComponent();
  271. const trimTime = (t) => (t ? t.slice(0, 5) : null);
  272. const { form } = useFormUpdateTracker({
  273. protocol: props.contract?.protocol ?? null,
  274. signature_date: props.contract?.signature_date ?? null,
  275. end_date: props.contract?.end_date ?? null,
  276. package_id: props.contract?.class_package_unit_id ?? null,
  277. class_quantity: props.contract?.class_quantity ?? null,
  278. weekday: props.contract?.weekday ?? null,
  279. start_time: trimTime(props.contract?.start_time),
  280. end_time: trimTime(props.contract?.end_time),
  281. second_weekday: props.contract?.second_weekday ?? null,
  282. second_start_time: trimTime(props.contract?.second_start_time),
  283. second_end_time: trimTime(props.contract?.second_end_time),
  284. due_date: props.contract?.due_date ?? null,
  285. enrollment_fee: props.contract?.tax_register ?? null,
  286. total_classes: props.contract?.class_quantity ?? null,
  287. down_payment: props.contract?.down_payment ?? null,
  288. installments: props.contract?.installments ?? null,
  289. early_payment_discount: props.contract?.early_payment_discount ?? null,
  290. material_value: props.contract?.material_value ?? null,
  291. material_installments: props.contract?.material_installments ?? null,
  292. interest_rate: props.contract?.interest_rate ?? null,
  293. payment_method: props.contract?.payment_method ?? null,
  294. late_fee: props.contract?.fine_cancelled ?? null,
  295. });
  296. const installmentOptions = Array.from({ length: 12 }, (_, i) => ({
  297. value: i + 1,
  298. label: `${i + 1}x`,
  299. }));
  300. const paymentMethods = [
  301. { value: "pix", label: "Pix" },
  302. { value: "credit_card", label: "Cartão de Crédito" },
  303. { value: "debit_card", label: "Cartão de Débito" },
  304. ];
  305. const weekdays = [
  306. { value: 1, label: "Segunda" },
  307. { value: 2, label: "Terça" },
  308. { value: 3, label: "Quarta" },
  309. { value: 4, label: "Quinta" },
  310. { value: 5, label: "Sexta" },
  311. { value: 6, label: "Sábado" },
  312. { value: 0, label: "Domingo" },
  313. ];
  314. const packages = ref([]);
  315. onMounted(async () => {
  316. packages.value = await getUnitPackages();
  317. });
  318. watch(
  319. () => form.package_id,
  320. (id) => {
  321. const pkg = packages.value.find((p) => p.id === id);
  322. if (!pkg) return;
  323. form.class_quantity = pkg.quantity_classes;
  324. form.total_classes = pkg.quantity_classes;
  325. form.enrollment_fee = pkg.contract_register_value;
  326. },
  327. );
  328. const formattedBirthDate = computed(() =>
  329. props.student.birth_date ? formatDateYMDtoDMY(props.student.birth_date) : "",
  330. );
  331. const { loading: saving, execute } = useSubmitHandler({
  332. onSuccess: () => onDialogOK(true),
  333. });
  334. function buildPayload() {
  335. return {
  336. student_id: props.student.id,
  337. protocol: form.protocol,
  338. signature_date: form.signature_date ? formatDateDMYtoYMD(form.signature_date) : null,
  339. end_date: form.end_date ? formatDateDMYtoYMD(form.end_date) : null,
  340. class_package_unit_id: form.package_id,
  341. class_quantity: form.class_quantity,
  342. weekday: form.weekday,
  343. start_time: form.start_time,
  344. end_time: form.end_time,
  345. second_weekday: form.second_weekday,
  346. second_start_time: form.second_start_time,
  347. second_end_time: form.second_end_time,
  348. due_date: form.due_date ? formatDateDMYtoYMD(form.due_date) : null,
  349. tax_register: form.enrollment_fee,
  350. down_payment: form.down_payment ?? 0,
  351. installments: form.installments,
  352. early_payment_discount: form.early_payment_discount,
  353. material_value: form.material_value,
  354. material_installments: form.material_installments,
  355. interest_rate: form.interest_rate,
  356. payment_method: form.payment_method,
  357. fine_cancelled: form.late_fee,
  358. };
  359. }
  360. async function handleSave() {
  361. const payload = buildPayload();
  362. await execute(() =>
  363. props.contract
  364. ? updateStudentContract(props.contract.id, payload)
  365. : createStudentContract(payload),
  366. );
  367. }
  368. </script>