From 4460d66f39d74802708a4ef69b29aca37ed95399 Mon Sep 17 00:00:00 2001 From: Horacio Daniel Ros Date: Tue, 15 Sep 2026 11:07:12 -0300 Subject: [PATCH] feat: agregar validaciones de acceso a pagos y mejorar la estructura del componente de perfil fiscal --- .../OrganizationFiscalProfiles.ts | 42 ++++ .../org/profile/[id]/fiscal/page.tsx | 225 +++++++++--------- 2 files changed, 156 insertions(+), 111 deletions(-) diff --git a/server/src/Models/OrganizationFiscalProfiles/OrganizationFiscalProfiles.ts b/server/src/Models/OrganizationFiscalProfiles/OrganizationFiscalProfiles.ts index 5d7f037..501404c 100644 --- a/server/src/Models/OrganizationFiscalProfiles/OrganizationFiscalProfiles.ts +++ b/server/src/Models/OrganizationFiscalProfiles/OrganizationFiscalProfiles.ts @@ -1,4 +1,7 @@ import { validatePermissionsByCompany, validateSessionUser } from "../../helpers/check"; +import CompaniesList from "../Companies/Companies"; +import PlanSubscriptionsList from "../PlanSubscriptions/PlanSubscriptons"; +import { PlanFeatures } from "../Plans/Plans.interface"; import { findArcaActivityByCode } from "../ArcaActivities/ArcaActivities.Catalog"; import { OrganizationFiscalProfilesAdapterMongoose } from "./OrganizationFiscalProfiles.Adapter.Mongoose"; import { @@ -70,6 +73,21 @@ class OrganizationFiscalProfilesManager implements IOrganizationFiscalProfilesMa await validatePermissionsByCompany({ companyId: data.companyId, sessionUser: data.sessionUser }); + const company = await CompaniesList.companies.findOne({ _id: data.companyId }); + + if (!company) { + throw new Error("No se ha encontrado la organización"); + } + + const canAccessPayments = await PlanSubscriptionsList.checkFeature({ + userId: String(company.ownerId), + feature: PlanFeatures.PAYMENTS, + }); + + if (!canAccessPayments) { + throw new Error("La organización no tiene un plan que soporte el módulo de pagos"); + } + this.validateProfileFields(data); const existingProfile = await this.organizationFiscalProfiles.findOne({ @@ -140,6 +158,18 @@ class OrganizationFiscalProfilesManager implements IOrganizationFiscalProfilesMa companyId: data.companyId, sessionUser, }); + + const company = await CompaniesList.companies.findOne({ _id: data.companyId }); + if (company) { + const canAccessPayments = await PlanSubscriptionsList.checkFeature({ + userId: String(company.ownerId), + feature: PlanFeatures.PAYMENTS, + }); + + if (!canAccessPayments) { + throw new Error("La organización no tiene un plan que soporte el módulo de pagos"); + } + } } else if (data.id) { const profile = await this.organizationFiscalProfiles.findOne(data); @@ -152,6 +182,18 @@ class OrganizationFiscalProfilesManager implements IOrganizationFiscalProfilesMa sessionUser, }); + const company = await CompaniesList.companies.findOne({ _id: String(profile.companyId) }); + if (company) { + const canAccessPayments = await PlanSubscriptionsList.checkFeature({ + userId: String(company.ownerId), + feature: PlanFeatures.PAYMENTS, + }); + + if (!canAccessPayments) { + throw new Error("La organización no tiene un plan que soporte el módulo de pagos"); + } + } + return [profile]; } else { throw new Error("Se requiere una organización para buscar perfiles fiscales"); diff --git a/txclient/src/app/admin/(organization-profile)/org/profile/[id]/fiscal/page.tsx b/txclient/src/app/admin/(organization-profile)/org/profile/[id]/fiscal/page.tsx index 3b2f4cd..f8e1e88 100644 --- a/txclient/src/app/admin/(organization-profile)/org/profile/[id]/fiscal/page.tsx +++ b/txclient/src/app/admin/(organization-profile)/org/profile/[id]/fiscal/page.tsx @@ -17,6 +17,7 @@ import ReceiptLongOutlinedIcon from "@mui/icons-material/ReceiptLongOutlined"; import { Grid2 } from "@mui/material"; import { ApiError } from "@models/Server.Error.model"; import style from "./page.module.css"; +import SecureByPlan, { SECURE_CONTENTS } from "@core/app/components/SecureByPlan/SecureByPlan"; import { findArcaActivities, findOrganizationFiscalProfiles, @@ -188,123 +189,125 @@ export default function OrganizationFiscalProfileEdit() { return ( <> -
-
-
- -
-
-

Configuración fiscal

-

- Estos datos se usan para emitir Factura C desde los movimientos de caja y para precargar el asistente ARCA. -

-
-
+ +
+
+
+ +
+
+

Configuración fiscal

+

+ Estos datos se usan para emitir Factura C desde los movimientos de caja y para precargar el asistente ARCA. +

+
+
-
-

Datos del emisor

- - - { - setCuit(event.target.value.replace(/\D/g, "")); - markNeedsSave(); - }} - /> - - - { - setLegalName(event.target.value); - markNeedsSave(); - }} - /> - - -
+
+

Datos del emisor

+ + + { + setCuit(event.target.value.replace(/\D/g, "")); + markNeedsSave(); + }} + /> + + + { + setLegalName(event.target.value); + markNeedsSave(); + }} + /> + + +
-
-
-

Condición fiscal

-

Seleccioná la condición declarada para esta organización.

-
-
- {fiscalConditionOptions.map((option) => { - const selected = taxCondition === option.id; +
+
+

Condición fiscal

+

Seleccioná la condición declarada para esta organización.

+
+
+ {fiscalConditionOptions.map((option) => { + const selected = taxCondition === option.id; - return ( - - ); - })} -
-
- -
-

Facturación ARCA

- - - { - setPointOfSale(event.target.value.replace(/\D/g, "")); - markNeedsSave(); - }} - /> - - - - {fiscalDataLoaded && ( - { - const selectedActivity = arcaActivities.find( - (activity) => activity.id === selectedActivityCode + return ( + ); + })} +
+
- setActivityCode(selectedActivity?.id || ""); - setActivityDescription(selectedActivity?.name || ""); - markNeedsSave(); - }} - /> - )} +
+

Facturación ARCA

+ + + { + setPointOfSale(event.target.value.replace(/\D/g, "")); + markNeedsSave(); + }} + /> + + -

- {activityCode && activityDescription - ? `${activityCode} - ${activityDescription}` - : "Seleccioná la actividad principal declarada en ARCA."} -

-
-
+ {fiscalDataLoaded && ( + { + const selectedActivity = arcaActivities.find( + (activity) => activity.id === selectedActivityCode + ); + + setActivityCode(selectedActivity?.id || ""); + setActivityDescription(selectedActivity?.name || ""); + markNeedsSave(); + }} + /> + )} + +

+ {activityCode && activityDescription + ? `${activityCode} - ${activityDescription}` + : "Seleccioná la actividad principal declarada en ARCA."} +

+ +
+ ); }