Quellcode durchsuchen

feat: update contract dialog to include pricing mode and materials details

alvesantos vor 10 Stunden
Ursprung
Commit
9204a4ef43

+ 248 - 7
src/pages/students/components/AddEditContractDialog.vue

@@ -143,7 +143,7 @@
                   />
                 </div>
 
-                <div class="col-5">
+                <div class="col-4">
                   <DefaultSelect
                     v-model="form.package_id"
                     v-model:error="validationErrors.class_package_unit_id"
@@ -157,7 +157,25 @@
                   />
                 </div>
 
-                <div class="col-7">
+                <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"
@@ -170,7 +188,7 @@
 
                 <div class="col-6">
                   <DefaultCurrencyInput
-                    :model-value="form.package_value"
+                    :model-value="form.total_value"
                     disable
                     label="Total do Contrato"
                   />
@@ -379,6 +397,100 @@
                 </div>
               </div>
 
+              <div class="text-subtitle2 q-mt-md q-mb-sm text-grey-8">
+                Materiais
+              </div>
+
+              <div class="row q-col-gutter-sm">
+                <div class="col-3">
+                  <DefaultCurrencyInput
+                    v-model="form.materials_value"
+                    v-model:error="validationErrors.materials_value"
+                    label="Valor dos Materiais"
+                    :disable="!props.contract"
+                  />
+                </div>
+
+                <div class="col-3">
+                  <DefaultSelect
+                    v-model="form.materials_installments"
+                    v-model:error="validationErrors.materials_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.materials_installments"
+                    :model-value="materialsInstallmentValue"
+                    disable
+                    label="Valor da Parcela"
+                  />
+                </div>
+
+                <div class="col-3">
+                  <DefaultInputDatePicker
+                    v-model="form.materials_due_date"
+                    v-model:error="validationErrors.materials_due_date"
+                    label="Data 1ª Parcela"
+                    :rules="[inputRules.date]"
+                  />
+                </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">
                   <DefaultInput
@@ -588,6 +700,42 @@ const packageInstallmentValue = computed(() => {
   return Number((form.package_value / form.package_installments).toFixed(2));
 });
 
+const materialsInstallmentValue = computed(() => {
+  if (!form.materials_value || !form.materials_installments) {
+    return null;
+  }
+
+  return Number(
+    (form.materials_value / form.materials_installments).toFixed(2),
+  );
+});
+
+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 = (() => {
@@ -683,8 +831,18 @@ const { form, getUpdatedFields } = useForm({
 
   package_value: props.contract?.package_value ?? null,
   payment_method: props.contract?.payment_method ?? null,
+
+  materials_due_date: props.contract?.materials_due_date ?? null,
+  materials_installments: props.contract?.materials_installments ?? null,
+  materials_value: props.contract?.materials_value ?? 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),
@@ -760,6 +918,12 @@ const buildPayload = () => ({
   installments: form.installments,
   interest_rate: form.interest_rate,
 
+  materials_due_date: form.materials_due_date
+    ? formatDateDMYtoYMD(form.materials_due_date)
+    : null,
+  materials_installments: form.materials_installments,
+  materials_value: form.materials_value,
+
   package_due_date: form.package_due_date
     ? formatDateDMYtoYMD(form.package_due_date)
     : null,
@@ -767,6 +931,7 @@ const buildPayload = () => ({
   package_installments: form.package_installments,
   package_value: form.package_value,
   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,
@@ -779,6 +944,13 @@ const buildPayload = () => ({
   start_time: form.start_time,
   student_id: currentStudent.value?.id ?? null,
   tax_register: form.enrollment_fee,
+
+  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,
 });
 
@@ -846,6 +1018,11 @@ const handleSave = async () => {
       "early_payment_discount",
       "interest_rate",
       "payment_method",
+      "pricing_mode",
+      "materials_value",
+      "materials_installments",
+      "total_value",
+      "total_installments",
     ]) {
       if (key in changedFields) {
         updatePayload[key] = payload[key];
@@ -863,6 +1040,8 @@ const handleSave = async () => {
       "end_date",
       "enrollment_due_date",
       "package_due_date",
+      "materials_due_date",
+      "total_due_date",
     ]) {
       if (key in changedFields) {
         updatePayload[key] = payload[key];
@@ -1003,10 +1182,6 @@ watch(
 
     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);
@@ -1024,6 +1199,72 @@ watch(
       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.enrollment_fee = null;
+      form.installments = null;
+      form.package_value = null;
+      form.package_installments = null;
+      form.materials_value = null;
+      form.materials_installments = null;
+      form.total_value = null;
+      form.total_installments = null;
+
+      return;
+    }
+
+    form.enrollment_fee = modality.registration_included_in_course
+      ? null
+      : modality.registration_value;
+
+    form.installments = modality.registration_included_in_course
+      ? null
+      : modality.registration_max_installments;
+
+    form.package_value = modality.classes_included_in_course
+      ? null
+      : modality.classes_value;
+
+    form.package_installments = modality.classes_included_in_course
+      ? null
+      : modality.classes_max_installments;
+
+    form.materials_value = modality.materials_included_in_course
+      ? null
+      : modality.materials_total_value;
+
+    form.materials_installments = modality.materials_included_in_course
+      ? null
+      : modality.materials_max_installments;
+
+    form.total_value = modality.total_value;
+    form.total_installments = modality.total_max_installments;
   },
 );
 

+ 34 - 14
src/pages/students/components/ViewContractDialog.vue

@@ -109,7 +109,7 @@
                 />
               </div>
 
-              <div class="col-5">
+              <div class="col-4">
                 <DefaultInput
                   :model-value="contract.package_name"
                   disable
@@ -117,7 +117,15 @@
                 />
               </div>
 
-              <div class="col-7">
+              <div class="col-3">
+                <DefaultInput
+                  :model-value="contract.pricing_mode_label"
+                  disable
+                  label="Modalidade"
+                />
+              </div>
+
+              <div class="col-5">
                 <DefaultInput
                   :model-value="contract.class_quantity"
                   disable
@@ -127,7 +135,7 @@
 
               <div class="col-6">
                 <DefaultCurrencyInput
-                  :model-value="contract.package_value"
+                  :model-value="contract.total_value"
                   disable
                   label="Total do Contrato"
                 />
@@ -235,14 +243,6 @@
                 />
               </div>
 
-              <div class="col-3">
-                <DefaultCurrencyInput
-                  :model-value="contract.down_payment"
-                  disable
-                  label="Entrada"
-                />
-              </div>
-
               <div class="col-3">
                 <DefaultInput
                   :model-value="
@@ -265,7 +265,7 @@
 
               <div class="col-3">
                 <DefaultCurrencyInput
-                  :model-value="contract.material_value"
+                  :model-value="contract.materials_value"
                   disable
                   label="Valor dos Materiais"
                 />
@@ -274,8 +274,8 @@
               <div class="col-3">
                 <DefaultInput
                   :model-value="
-                    contract.material_installments
-                      ? `${contract.material_installments}x`
+                    contract.materials_installments
+                      ? `${contract.materials_installments}x`
                       : null
                   "
                   disable
@@ -291,6 +291,26 @@
                 />
               </div>
 
+              <div class="col-3">
+                <DefaultCurrencyInput
+                  :model-value="contract.total_value"
+                  disable
+                  label="Valor Total do Curso"
+                />
+              </div>
+
+              <div class="col-3">
+                <DefaultInput
+                  :model-value="
+                    contract.total_installments
+                      ? `${contract.total_installments}x`
+                      : null
+                  "
+                  disable
+                  label="Parcelas"
+                />
+              </div>
+
               <div class="col-6">
                 <DefaultInput
                   :model-value="