feat: agregar validaciones para el módulo ARCA según el plan de la organización
This commit is contained in:
@@ -2,6 +2,9 @@ import crypto from "crypto";
|
||||
import axios from "axios";
|
||||
import forge from "node-forge";
|
||||
import { validatePermissionsByCompany, validateSessionUser } from "../../helpers/check";
|
||||
import CompaniesList from "../Companies/Companies";
|
||||
import PlanSubscriptionsList from "../PlanSubscriptions/PlanSubscriptons";
|
||||
import { PlanFeatures } from "../Plans/Plans.interface";
|
||||
import ArcaWsaaTokens from "../ArcaWsaaTokens/ArcaWsaaTokens";
|
||||
import {
|
||||
ARCA_WSAA_ENVIRONMENT,
|
||||
@@ -123,6 +126,23 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
await validatePermissionsByCompany({ companyId, sessionUser });
|
||||
}
|
||||
|
||||
private async requirePaymentsFeature(companyId: string): Promise<void> {
|
||||
const company = await CompaniesList.companies.findOne({ _id: 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");
|
||||
}
|
||||
}
|
||||
|
||||
private async requireActiveFiscalProfile(
|
||||
companyId: string,
|
||||
sessionUser: string
|
||||
@@ -730,6 +750,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
|
||||
public async generateCsr(data: GenerateArcaCredentialCsrParams): Promise<ArcaCredentialView> {
|
||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||
await this.requirePaymentsFeature(data.companyId);
|
||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||
|
||||
const keys = forge.pki.rsa.generateKeyPair(2048);
|
||||
@@ -778,6 +799,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
|
||||
public async uploadCertificate(data: UploadArcaCredentialCertificateParams): Promise<ArcaCredentialView> {
|
||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||
await this.requirePaymentsFeature(data.companyId);
|
||||
|
||||
if (!data.certificatePem || !data.certificatePem.trim()) {
|
||||
throw new Error("Se requiere el certificado ARCA");
|
||||
@@ -836,6 +858,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
|
||||
public async testWsaaLogin(data: TestArcaWsaaLoginParams): Promise<ArcaWsaaLoginTestResult> {
|
||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||
await this.requirePaymentsFeature(data.companyId);
|
||||
|
||||
const service = ARCA_WSAA_SERVICE.WSFE;
|
||||
const environment = this.getArcaEnvironment();
|
||||
@@ -853,6 +876,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
|
||||
public async getWsfeLastVoucher(data: ArcaWsfeLastVoucherParams): Promise<ArcaWsfeLastVoucherResult> {
|
||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||
await this.requirePaymentsFeature(data.companyId);
|
||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||
const environment = this.getArcaEnvironment();
|
||||
@@ -886,6 +910,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
|
||||
public async issueFacturaC(data: ArcaWsfeIssueFacturaCParams): Promise<ArcaWsfeIssueFacturaCResult> {
|
||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||
await this.requirePaymentsFeature(data.companyId);
|
||||
|
||||
if (data.amount <= 0) {
|
||||
throw new Error("El importe de la factura debe ser mayor a cero");
|
||||
@@ -961,6 +986,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
|
||||
public async getWsfeDiagnostics(data: ArcaWsfeDiagnosticsParams): Promise<ArcaWsfeDiagnosticsResult> {
|
||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||
await this.requirePaymentsFeature(data.companyId);
|
||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||
const environment = this.getArcaEnvironment();
|
||||
@@ -1054,8 +1080,11 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
|
||||
await validateSessionUser({ sessionUser: data.sessionUser });
|
||||
|
||||
let companyId: string | undefined;
|
||||
|
||||
if (data.companyId) {
|
||||
await validatePermissionsByCompany({ companyId: data.companyId, sessionUser: data.sessionUser });
|
||||
companyId = data.companyId;
|
||||
} else if (data.id) {
|
||||
const credential = await this.arcaCredentials.findOne(data);
|
||||
|
||||
@@ -1067,12 +1096,13 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
||||
companyId: String(credential.companyId),
|
||||
sessionUser: data.sessionUser,
|
||||
});
|
||||
|
||||
return [this.sanitizeCredential(credential)];
|
||||
companyId = String(credential.companyId);
|
||||
} else {
|
||||
throw new Error("Se requiere una organización para buscar credenciales ARCA");
|
||||
}
|
||||
|
||||
await this.requirePaymentsFeature(companyId);
|
||||
|
||||
const credentials = await this.arcaCredentials.find(data);
|
||||
return credentials.map((credential) => this.sanitizeCredential(credential));
|
||||
}
|
||||
|
||||
@@ -206,6 +206,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
const [wapError, setWapError] = useState<string | null>(null);
|
||||
|
||||
// State for ARCA onboarding flow
|
||||
const [arcaIsBlocked, setArcaIsBlocked] = useState(false);
|
||||
const [arcaCuit, setArcaCuit] = useState("");
|
||||
const [arcaLegalName, setArcaLegalName] = useState("");
|
||||
const [arcaTaxCondition, setArcaTaxCondition] = useState<"MONOTRIBUTO" | "EXENTO">("MONOTRIBUTO");
|
||||
@@ -277,6 +278,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
};
|
||||
|
||||
const loadOrganizationContext = (org: any) => {
|
||||
setArcaIsBlocked(false);
|
||||
setCreatedCompanyId(org.id || org._id);
|
||||
setSelectedOrganization(org);
|
||||
setOrgName(org.name || "");
|
||||
@@ -499,6 +501,18 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
setWapQr(null);
|
||||
setWapError(null);
|
||||
} else if (flowId === "arca-onboarding") {
|
||||
const orgSub = SessionInfo.organizationSubscriptions[org.id || org._id];
|
||||
const planSupportsPayments = orgSub ? orgSub.plan?.payments : false;
|
||||
|
||||
if (!planSupportsPayments) {
|
||||
setArcaIsBlocked(true);
|
||||
setCurrentStepIndex(0);
|
||||
setArcaError(null);
|
||||
setArcaLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setArcaIsBlocked(false);
|
||||
setCurrentStepIndex(0);
|
||||
setArcaError(null);
|
||||
setArcaLoading(false);
|
||||
@@ -527,6 +541,19 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
setArcaStatusLabel(getArcaStatusLabel(credential.status));
|
||||
}).catch(console.error);
|
||||
} else if (flowId === "arca-test-connection") {
|
||||
const orgSub = SessionInfo.organizationSubscriptions[org.id || org._id];
|
||||
const planSupportsPayments = orgSub ? orgSub.plan?.payments : false;
|
||||
|
||||
if (!planSupportsPayments) {
|
||||
setArcaIsBlocked(true);
|
||||
setCurrentStepIndex(0);
|
||||
setArcaError(null);
|
||||
setArcaLoading(false);
|
||||
setArcaTestResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setArcaIsBlocked(false);
|
||||
setCurrentStepIndex(0);
|
||||
setArcaError(null);
|
||||
setArcaLoading(false);
|
||||
@@ -2415,8 +2442,42 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
if (flowId === "public-link") return renderPublicLinkFlow();
|
||||
if (flowId === "notifications") return renderNotificationsFlow();
|
||||
if (flowId === "whatsapp-bot") return renderWhatsAppBotFlow();
|
||||
if (flowId === "arca-onboarding") return renderArcaOnboardingFlow();
|
||||
if (flowId === "arca-test-connection") return renderArcaTestConnectionFlow();
|
||||
if (flowId === "arca-onboarding") {
|
||||
if (arcaIsBlocked) {
|
||||
return (
|
||||
<QuestionCard
|
||||
isActive
|
||||
title="Módulo ARCA no disponible"
|
||||
description="Este flujo requiere un plan con el módulo de pagos habilitado para emitir Factura C."
|
||||
onNext={() => router.push("/landing/pricing")}
|
||||
nextLabel="Ver planes"
|
||||
>
|
||||
<div style={{ color: "rgba(255,255,255,0.75)", textAlign: "center", lineHeight: 1.6 }}>
|
||||
La organización seleccionada necesita un plan con el módulo de pagos activo para usar ARCA. Actualizá tu plan o volvé al asistente y elegí otra opción.
|
||||
</div>
|
||||
</QuestionCard>
|
||||
);
|
||||
}
|
||||
return renderArcaOnboardingFlow();
|
||||
}
|
||||
if (flowId === "arca-test-connection") {
|
||||
if (arcaIsBlocked) {
|
||||
return (
|
||||
<QuestionCard
|
||||
isActive
|
||||
title="Test de conexión ARCA no disponible"
|
||||
description="Este flujo requiere un plan con el módulo de pagos habilitado para probar la conexión con ARCA."
|
||||
onNext={() => router.push("/landing/pricing")}
|
||||
nextLabel="Ver planes"
|
||||
>
|
||||
<div style={{ color: "rgba(255,255,255,0.75)", textAlign: "center", lineHeight: 1.6 }}>
|
||||
La organización seleccionada necesita un plan con el módulo de pagos activo para usar ARCA. Actualizá tu plan o volvé al asistente y elegí otra opción.
|
||||
</div>
|
||||
</QuestionCard>
|
||||
);
|
||||
}
|
||||
return renderArcaTestConnectionFlow();
|
||||
}
|
||||
if (flowId === "disable-schedule") return renderDisableScheduleFlow();
|
||||
if (flowId === "open-reservation-periods") return renderReservationPeriodFlow();
|
||||
if (flowId === "override-schedule") return renderOverrideScheduleFlow();
|
||||
|
||||
Reference in New Issue
Block a user