Explorar el Código

feat(financial): update API routes to franchisee-specific endpoints and enhance permission checks across financial components

ebagabee hace 1 semana
padre
commit
487a35a59f

+ 4 - 4
src/api/financial_plan_account.js

@@ -1,21 +1,21 @@
 import api from "src/api";
 
 export const getPlanAccountsMe = async () => {
-  const { data } = await api.get("/financial-plan-account/me");
+  const { data } = await api.get("/franchisee/financial/plan-accounts");
   return data.payload;
 };
 
 export const createPlanAccountMe = async (payload) => {
-  const { data } = await api.post("/financial-plan-account/me", payload);
+  const { data } = await api.post("/franchisee/financial/plan-accounts", payload);
   return data.payload;
 };
 
 export const updatePlanAccountMe = async (id, payload) => {
-  const { data } = await api.put(`/financial-plan-account/me/${id}`, payload);
+  const { data } = await api.put(`/franchisee/financial/plan-accounts/${id}`, payload);
   return data.payload;
 };
 
 export const deletePlanAccountMe = async (id) => {
-  const { data } = await api.delete(`/financial-plan-account/me/${id}`);
+  const { data } = await api.delete(`/franchisee/financial/plan-accounts/${id}`);
   return data;
 };

+ 6 - 6
src/api/treasury.js

@@ -1,33 +1,33 @@
 import api from "src/api";
 
 export const getTreasuryAccountsMe = async () => {
-  const { data } = await api.get("/treasury-account/me");
+  const { data } = await api.get("/franchisee/financial/treasury/accounts");
   return data.payload;
 };
 
 export const createTreasuryAccountMe = async (payload) => {
-  const { data } = await api.post("/treasury-account/me", payload);
+  const { data } = await api.post("/franchisee/financial/treasury/accounts", payload);
   return data.payload;
 };
 
 export const updateTreasuryAccountMe = async (id, payload) => {
-  const { data } = await api.put(`/treasury-account/me/${id}`, payload);
+  const { data } = await api.put(`/franchisee/financial/treasury/accounts/${id}`, payload);
   return data.payload;
 };
 
 export const deleteTreasuryAccountMe = async (id) => {
-  const { data } = await api.delete(`/treasury-account/me/${id}`);
+  const { data } = await api.delete(`/franchisee/financial/treasury/accounts/${id}`);
   return data;
 };
 
 export const getTreasuryLaunchesMe = async (accountId) => {
-  const { data } = await api.get("/treasury-launch/me", {
+  const { data } = await api.get("/franchisee/financial/treasury/launches", {
     params: accountId ? { account_id: accountId } : {},
   });
   return data.payload;
 };
 
 export const createTreasuryLaunchMe = async (payload) => {
-  const { data } = await api.post("/treasury-launch/me", payload);
+  const { data } = await api.post("/franchisee/financial/treasury/launches", payload);
   return data.payload;
 };

+ 1 - 9
src/api/unit_financial.js

@@ -1,14 +1,6 @@
 import api from "src/api";
 
-export const getFinancialByUnit = async (unitId) => {
-  const { data } = await api.get("/unit-financial", {
-    params: { unit_id: unitId },
-  });
-  return data.payload;
-};
-
 export const getFinancialMe = async () => {
-  const { data } = await api.get("/unit-financial/me");
+  const { data } = await api.get("/franchisee/financial/settings");
   return data.payload;
 };
-

+ 1 - 1
src/api/unit_financial_dashboard.js

@@ -1,6 +1,6 @@
 import api from "src/api";
 
 export const getFinancialOverviewMe = async () => {
-  const { data } = await api.get("/unit-financial-dashboard/me");
+  const { data } = await api.get("/franchisee/financial/overview");
   return data.payload;
 };

+ 4 - 4
src/api/unit_payable.js

@@ -1,21 +1,21 @@
 import api from "src/api";
 
 export const getPayablesMe = async (params = {}) => {
-  const { data } = await api.get("/unit-payable/me", { params });
+  const { data } = await api.get("/franchisee/financial/payables", { params });
   return data.payload;
 };
 
 export const createPayableMe = async (payload) => {
-  const { data } = await api.post("/unit-payable/me", payload);
+  const { data } = await api.post("/franchisee/financial/payables", payload);
   return data.payload;
 };
 
 export const settlePayableMe = async (id, payload = {}) => {
-  const { data } = await api.post(`/unit-payable/me/${id}/settle`, payload);
+  const { data } = await api.post(`/franchisee/financial/payables/${id}/settle`, payload);
   return data.payload;
 };
 
 export const reopenPayableMe = async (id) => {
-  const { data } = await api.post(`/unit-payable/me/${id}/reopen`);
+  const { data } = await api.post(`/franchisee/financial/payables/${id}/reopen`);
   return data.payload;
 };

+ 7 - 7
src/api/unit_receivable.js

@@ -1,37 +1,37 @@
 import api from "src/api";
 
 export const getReceivablesMe = async (params = {}) => {
-  const { data } = await api.get("/unit-receivable/me", { params });
+  const { data } = await api.get("/franchisee/financial/receivables", { params });
   return data.payload;
 };
 
 export const settleReceivableMe = async (id, payload = {}) => {
-  const { data } = await api.post(`/unit-receivable/me/${id}/settle`, payload);
+  const { data } = await api.post(`/franchisee/financial/receivables/${id}/settle`, payload);
   return data.payload;
 };
 
 export const reopenReceivableMe = async (id) => {
-  const { data } = await api.post(`/unit-receivable/me/${id}/reopen`);
+  const { data } = await api.post(`/franchisee/financial/receivables/${id}/reopen`);
   return data.payload;
 };
 
 export const chargeReceivableMe = async (id) => {
-  const { data } = await api.post(`/unit-receivable/me/${id}/charge`);
+  const { data } = await api.post(`/franchisee/financial/receivables/${id}/charge`);
   return data.payload;
 };
 
 // Recebíveis avulsos (manuais)
 export const createManualReceivableMe = async (payload) => {
-  const { data } = await api.post("/unit-receivable/me/manual", payload);
+  const { data } = await api.post("/franchisee/financial/receivables", payload);
   return data.payload;
 };
 
 export const settleManualReceivableMe = async (id, payload = {}) => {
-  const { data } = await api.post(`/unit-receivable/me/manual/${id}/settle`, payload);
+  const { data } = await api.post(`/franchisee/financial/receivables/manual/${id}/settle`, payload);
   return data.payload;
 };
 
 export const reopenManualReceivableMe = async (id) => {
-  const { data } = await api.post(`/unit-receivable/me/manual/${id}/reopen`);
+  const { data } = await api.post(`/franchisee/financial/receivables/manual/${id}/reopen`);
   return data.payload;
 };

+ 12 - 2
src/pages/financial/AccountsPayablePage.vue

@@ -39,6 +39,7 @@
         @update:model-value="loadData"
       />
       <q-btn
+        v-if="canAdd"
         color="primary"
         label="Nova conta"
         icon="mdi-plus"
@@ -89,7 +90,7 @@
         <template #body-cell-actions="{ row }">
           <q-td auto-width>
             <q-btn
-              v-if="row.status !== 'paid' && row.status !== 'cancelled'"
+              v-if="canEdit && row.status !== 'paid' && row.status !== 'cancelled'"
               color="primary-2"
               label="Dar baixa"
               dense
@@ -99,7 +100,7 @@
               @click="openSettle(row)"
             />
             <q-btn
-              v-else-if="row.status === 'paid'"
+              v-else-if="canEdit && row.status === 'paid'"
               color="grey-7"
               label="Reabrir"
               dense
@@ -228,6 +229,15 @@ import DefaultCurrencyInput from "src/components/defaults/DefaultCurrencyInput.v
 import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePicker.vue";
 import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
 import FinancialCard from "src/components/financial/FinancialCard.vue";
+import { permissionStore } from "src/stores/permission";
+
+const permissions = permissionStore();
+const canAdd = computed(() =>
+  permissions.getAccess("franchisee_financial", "add"),
+);
+const canEdit = computed(() =>
+  permissions.getAccess("franchisee_financial", "edit"),
+);
 
 const rows = ref([]);
 const statusFilter = ref(null);

+ 18 - 3
src/pages/financial/AccountsReceivablePage.vue

@@ -39,6 +39,7 @@
         @update:model-value="loadData"
       />
       <q-btn
+        v-if="canAdd"
         color="primary"
         label="Nova conta a receber"
         icon="mdi-plus"
@@ -90,6 +91,7 @@
               />
               <q-btn
                 v-else-if="
+                  canEdit &&
                   row.source !== 'manual' &&
                   row.status !== 'paid' &&
                   row.status !== 'cancelled'
@@ -105,7 +107,7 @@
                 @click="onCharge(row)"
               />
               <q-btn
-                v-if="row.status !== 'paid' && row.status !== 'cancelled'"
+                v-if="canEdit && row.status !== 'paid' && row.status !== 'cancelled'"
                 color="primary-2"
                 label="Dar baixa"
                 dense
@@ -115,7 +117,7 @@
                 @click="openSettle(row)"
               />
               <q-btn
-                v-else-if="row.status === 'paid'"
+                v-else-if="canEdit && row.status === 'paid'"
                 color="grey-7"
                 label="Reabrir"
                 dense
@@ -163,6 +165,7 @@
               />
             </div>
             <DefaultSelect
+              v-if="canViewStudents"
               v-model="createForm.student_id"
               label="Aluno (opcional)"
               :options="studentOptions"
@@ -270,6 +273,7 @@ import {
 } from "src/api/unit_receivable";
 import { getStudents } from "src/api/student";
 import { getPlanAccountsMe } from "src/api/financial_plan_account";
+import { permissionStore } from "src/stores/permission";
 
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 import DefaultTable from "src/components/defaults/DefaultTable.vue";
@@ -280,6 +284,17 @@ import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePick
 import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
 import FinancialCard from "src/components/financial/FinancialCard.vue";
 
+const permissions = permissionStore();
+const canAdd = computed(() =>
+  permissions.getAccess("franchisee_financial", "add"),
+);
+const canEdit = computed(() =>
+  permissions.getAccess("franchisee_financial", "edit"),
+);
+const canViewStudents = computed(() =>
+  permissions.getAccess("franchisee_students", "view"),
+);
+
 const rows = ref([]);
 const statusFilter = ref(null);
 const settleDialog = ref(false);
@@ -393,7 +408,7 @@ async function loadAux() {
   loadingAux.value = true;
   try {
     const [students, planAccounts] = await Promise.all([
-      getStudents(),
+      canViewStudents.value ? getStudents() : Promise.resolve([]),
       getPlanAccountsMe(),
     ]);
     studentOptions.value = (students ?? []).map((s) => ({

+ 14 - 2
src/pages/financial/ChartOfAccountsPage.vue

@@ -4,6 +4,7 @@
 
     <div class="row justify-end items-center q-px-md q-mb-sm">
       <q-btn
+        v-if="canAdd"
         color="primary"
         label="Nova conta"
         icon="mdi-plus"
@@ -22,7 +23,7 @@
         descricao="contas"
         :feminino="true"
         :columns="columns"
-        :delete-function="onDelete"
+        :delete-function="canDelete ? onDelete : null"
       >
         <template #body-cell-parent="{ row }">
           <q-td class="text-left">
@@ -45,7 +46,7 @@
         <template #body-cell-actions="{ row }">
           <q-td auto-width>
             <q-btn
-              v-if="row.unit_id"
+              v-if="row.unit_id && canEdit"
               color="primary-2"
               label="Editar"
               dense
@@ -162,8 +163,19 @@ import DefaultTable from "src/components/defaults/DefaultTable.vue";
 import DefaultInput from "src/components/defaults/DefaultInput.vue";
 import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
 import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
+import { permissionStore } from "src/stores/permission";
 
 const $q = useQuasar();
+const permissions = permissionStore();
+const canAdd = computed(() =>
+  permissions.getAccess("franchisee_financial", "add"),
+);
+const canEdit = computed(() =>
+  permissions.getAccess("franchisee_financial", "edit"),
+);
+const canDelete = computed(() =>
+  permissions.getAccess("franchisee_financial", "delete"),
+);
 
 const rows = ref([]);
 const dialog = ref(false);

+ 11 - 0
src/pages/financial/TreasuryPage.vue

@@ -5,6 +5,7 @@
     <div class="row items-center justify-between q-px-md q-mb-sm">
       <div class="text-subtitle1 text-weight-medium">Bancos / Caixa</div>
       <q-btn
+        v-if="canAdd"
         color="primary"
         label="Nova conta"
         icon="mdi-plus"
@@ -32,6 +33,7 @@
             <div class="text-subtitle2 ellipsis">{{ acc.name }}</div>
             <q-space />
             <q-btn
+              v-if="canEdit"
               flat
               round
               dense
@@ -61,6 +63,7 @@
           Extrato — {{ selectedAccount?.name }}
         </div>
         <q-btn
+          v-if="canAdd"
           color="primary-2"
           label="Nova movimentação"
           icon="mdi-swap-vertical"
@@ -114,6 +117,7 @@ import {
 
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 import DefaultTable from "src/components/defaults/DefaultTable.vue";
+import { permissionStore } from "src/stores/permission";
 
 const AddEditTreasuryAccountDialog = defineAsyncComponent(
   () => import("src/pages/financial/components/AddEditTreasuryAccountDialog.vue"),
@@ -126,6 +130,13 @@ const accounts = ref([]);
 const launches = ref([]);
 const selectedId = ref(null);
 const $q = useQuasar();
+const permissions = permissionStore();
+const canAdd = computed(() =>
+  permissions.getAccess("franchisee_financial", "add"),
+);
+const canEdit = computed(() =>
+  permissions.getAccess("franchisee_financial", "edit"),
+);
 
 const launchColumns = [
   { name: "launch_date", label: "Data", field: "launch_date", align: "left" },

+ 6 - 1
src/pages/students/components/AddEditContractDialog.vue

@@ -365,6 +365,7 @@ import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
 import { formatDateYMDtoDMY, formatDateDMYtoYMD } from "src/helpers/utils";
 import { getUnitPackages } from "src/api/package";
 import { getFinancialMe } from "src/api/unit_financial";
+import { permissionStore } from "src/stores/permission";
 import {
   createStudentContract,
   updateStudentContract,
@@ -388,6 +389,10 @@ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
   useDialogPluginComponent();
 
 const $q = useQuasar();
+const permissions = permissionStore();
+const canViewFinancial = computed(() =>
+  permissions.getAccess("franchisee_financial", "view"),
+);
 const activeTab = ref("dados");
 const medias = ref([]);
 const loadingMedias = ref(false);
@@ -522,7 +527,7 @@ onMounted(async () => {
   packages.value = await getUnitPackages();
 
   // Para novo contrato, pré-preenche desconto/juros/multa com os defaults da unidade
-  if (!props.contract) {
+  if (!props.contract && canViewFinancial.value) {
     try {
       const financial = await getFinancialMe();
       if (financial) {

+ 6 - 1
src/pages/students/components/FreezeContractDialog.vue

@@ -95,6 +95,7 @@ import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue
 import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
 import { getContractInstallments, freezeContract } from "src/api/studentContract";
 import { getFinancialMe } from "src/api/unit_financial";
+import { permissionStore } from "src/stores/permission";
 
 const props = defineProps({
   contract: { type: Object, required: true },
@@ -106,6 +107,10 @@ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
   useDialogPluginComponent();
 
 const $q = useQuasar();
+const permissions = permissionStore();
+const canViewFinancial = computed(() =>
+  permissions.getAccess("franchisee_financial", "view"),
+);
 
 const loading        = ref(true);
 const saving         = ref(false);
@@ -185,7 +190,7 @@ onMounted(async () => {
   try {
     const [installments, financial] = await Promise.all([
       getContractInstallments(props.contract.id),
-      getFinancialMe(),
+      canViewFinancial.value ? getFinancialMe() : Promise.resolve(null),
     ]);
     pendingInstallments.value = installments ?? [];
     maxFreezeCount.value      = financial?.max_freeze_count ?? null;

+ 15 - 4
src/pages/unit/UnitActionPage.vue

@@ -21,7 +21,11 @@
 
       <ContractsTab v-show="activeTab === 'contracts'" :unit-id="unitId" />
 
-      <FinancialTab v-show="activeTab === 'financial'" :unit-id="unitId" />
+      <FinancialTab
+        v-if="canViewFinancial"
+        v-show="activeTab === 'financial'"
+        :unit-id="unitId"
+      />
 
       <HistoryTab v-show="activeTab === 'history'" :unit-id="unitId" />
 
@@ -34,6 +38,7 @@
 import { computed, ref } from "vue";
 import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
 import { userStore } from "src/stores/user";
+import { permissionStore } from "src/stores/permission";
 
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 import CustomTabComponent from "src/components/shared/CustomTabComponent.vue";
@@ -46,6 +51,10 @@ import PartnersTab from "src/pages/unit/tabs/PartnersTab.vue";
 import UnitDataTab from "src/pages/unit/tabs/UnitDataTab.vue";
 
 const store = userStore();
+const permissions = permissionStore();
+const canViewFinancial = computed(() =>
+  permissions.getAccess("franchisee_financial", "view"),
+);
 
 const { form, getFormAsFormData, setUpdateFormAsOriginal } =
   useFormUpdateTracker({
@@ -71,12 +80,14 @@ const activeTab = ref("unit_data");
 
 const unitId = computed(() => store.selectedUnit?.id ?? null);
 
-const allTabs = [
+const allTabs = computed(() => [
   { name: "unit_data", label: "Dados da Unidade" },
   { name: "partners", label: "Sócios" },
   { name: "contracts", label: "Contratos" },
-  { name: "financial", label: "Financeiro" },
+  ...(canViewFinancial.value
+    ? [{ name: "financial", label: "Financeiro" }]
+    : []),
   { name: "history", label: "Histórico" },
   { name: "medias", label: "Mídias" },
-];
+]);
 </script>

+ 5 - 0
src/router/routes/financial.route.js

@@ -6,6 +6,7 @@ export default [
     meta: {
       title: { value: "Financeiro", translate: false },
       requireAuth: true,
+      requiredPermission: "franchisee_financial",
       breadcrumbs: [
         { name: "DashboardPage", title: "Dashboard" },
         { name: "FinancialPage", title: "Financeiro" },
@@ -19,6 +20,7 @@ export default [
     meta: {
       title: { value: "Contas a Pagar", translate: false },
       requireAuth: true,
+      requiredPermission: "franchisee_financial",
       breadcrumbs: [
         { name: "DashboardPage", title: "Dashboard" },
         { name: "FinancialPage", title: "Financeiro" },
@@ -33,6 +35,7 @@ export default [
     meta: {
       title: { value: "Contas a Receber", translate: false },
       requireAuth: true,
+      requiredPermission: "franchisee_financial",
       breadcrumbs: [
         { name: "DashboardPage", title: "Dashboard" },
         { name: "FinancialPage", title: "Financeiro" },
@@ -47,6 +50,7 @@ export default [
     meta: {
       title: { value: "Plano de Contas", translate: false },
       requireAuth: true,
+      requiredPermission: "franchisee_financial",
       breadcrumbs: [
         { name: "DashboardPage", title: "Dashboard" },
         { name: "FinancialPage", title: "Financeiro" },
@@ -61,6 +65,7 @@ export default [
     meta: {
       title: { value: "Tesouraria", translate: false },
       requireAuth: true,
+      requiredPermission: "franchisee_financial",
       breadcrumbs: [
         { name: "DashboardPage", title: "Dashboard" },
         { name: "FinancialPage", title: "Financeiro" },

+ 12 - 6
src/stores/navigation.js

@@ -45,7 +45,8 @@ export const navigationStore = defineStore("navigation", () => {
       title: "Financeiro",
       icon: "mdi-finance",
       disable: false,
-      permission: true,
+      permission: false,
+      permissionScope: "franchisee_financial",
       childrens: [
         {
           type: "single",
@@ -53,7 +54,8 @@ export const navigationStore = defineStore("navigation", () => {
           name: "FinancialPage",
           icon: "mdi-view-dashboard-outline",
           disable: false,
-          permission: true,
+          permission: false,
+          permissionScope: "franchisee_financial",
         },
         {
           type: "single",
@@ -61,7 +63,8 @@ export const navigationStore = defineStore("navigation", () => {
           name: "TreasuryPage",
           icon: "mdi-bank-outline",
           disable: false,
-          permission: true,
+          permission: false,
+          permissionScope: "franchisee_financial",
         },
         {
           type: "single",
@@ -69,7 +72,8 @@ export const navigationStore = defineStore("navigation", () => {
           name: "AccountsReceivablePage",
           icon: "mdi-cash-plus",
           disable: false,
-          permission: true,
+          permission: false,
+          permissionScope: "franchisee_financial",
         },
         {
           type: "single",
@@ -77,7 +81,8 @@ export const navigationStore = defineStore("navigation", () => {
           name: "AccountsPayablePage",
           icon: "mdi-cash-minus",
           disable: false,
-          permission: true,
+          permission: false,
+          permissionScope: "franchisee_financial",
         },
         {
           type: "single",
@@ -85,7 +90,8 @@ export const navigationStore = defineStore("navigation", () => {
           name: "ChartOfAccountsPage",
           icon: "mdi-format-list-bulleted",
           disable: false,
-          permission: true,
+          permission: false,
+          permissionScope: "franchisee_financial",
         },
       ],
     },