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 axios from "axios";
|
||||||
import forge from "node-forge";
|
import forge from "node-forge";
|
||||||
import { validatePermissionsByCompany, validateSessionUser } from "../../helpers/check";
|
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 ArcaWsaaTokens from "../ArcaWsaaTokens/ArcaWsaaTokens";
|
||||||
import {
|
import {
|
||||||
ARCA_WSAA_ENVIRONMENT,
|
ARCA_WSAA_ENVIRONMENT,
|
||||||
@@ -123,6 +126,23 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
await validatePermissionsByCompany({ companyId, sessionUser });
|
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(
|
private async requireActiveFiscalProfile(
|
||||||
companyId: string,
|
companyId: string,
|
||||||
sessionUser: string
|
sessionUser: string
|
||||||
@@ -730,6 +750,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
|
|
||||||
public async generateCsr(data: GenerateArcaCredentialCsrParams): Promise<ArcaCredentialView> {
|
public async generateCsr(data: GenerateArcaCredentialCsrParams): Promise<ArcaCredentialView> {
|
||||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||||
|
await this.requirePaymentsFeature(data.companyId);
|
||||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||||
|
|
||||||
const keys = forge.pki.rsa.generateKeyPair(2048);
|
const keys = forge.pki.rsa.generateKeyPair(2048);
|
||||||
@@ -778,6 +799,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
|
|
||||||
public async uploadCertificate(data: UploadArcaCredentialCertificateParams): Promise<ArcaCredentialView> {
|
public async uploadCertificate(data: UploadArcaCredentialCertificateParams): Promise<ArcaCredentialView> {
|
||||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||||
|
await this.requirePaymentsFeature(data.companyId);
|
||||||
|
|
||||||
if (!data.certificatePem || !data.certificatePem.trim()) {
|
if (!data.certificatePem || !data.certificatePem.trim()) {
|
||||||
throw new Error("Se requiere el certificado ARCA");
|
throw new Error("Se requiere el certificado ARCA");
|
||||||
@@ -836,6 +858,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
|
|
||||||
public async testWsaaLogin(data: TestArcaWsaaLoginParams): Promise<ArcaWsaaLoginTestResult> {
|
public async testWsaaLogin(data: TestArcaWsaaLoginParams): Promise<ArcaWsaaLoginTestResult> {
|
||||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||||
|
await this.requirePaymentsFeature(data.companyId);
|
||||||
|
|
||||||
const service = ARCA_WSAA_SERVICE.WSFE;
|
const service = ARCA_WSAA_SERVICE.WSFE;
|
||||||
const environment = this.getArcaEnvironment();
|
const environment = this.getArcaEnvironment();
|
||||||
@@ -853,6 +876,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
|
|
||||||
public async getWsfeLastVoucher(data: ArcaWsfeLastVoucherParams): Promise<ArcaWsfeLastVoucherResult> {
|
public async getWsfeLastVoucher(data: ArcaWsfeLastVoucherParams): Promise<ArcaWsfeLastVoucherResult> {
|
||||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||||
|
await this.requirePaymentsFeature(data.companyId);
|
||||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||||
const environment = this.getArcaEnvironment();
|
const environment = this.getArcaEnvironment();
|
||||||
@@ -886,6 +910,7 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
|
|
||||||
public async issueFacturaC(data: ArcaWsfeIssueFacturaCParams): Promise<ArcaWsfeIssueFacturaCResult> {
|
public async issueFacturaC(data: ArcaWsfeIssueFacturaCParams): Promise<ArcaWsfeIssueFacturaCResult> {
|
||||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||||
|
await this.requirePaymentsFeature(data.companyId);
|
||||||
|
|
||||||
if (data.amount <= 0) {
|
if (data.amount <= 0) {
|
||||||
throw new Error("El importe de la factura debe ser mayor a cero");
|
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> {
|
public async getWsfeDiagnostics(data: ArcaWsfeDiagnosticsParams): Promise<ArcaWsfeDiagnosticsResult> {
|
||||||
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
await this.validateCompanyAdmin(data.companyId, data.sessionUser);
|
||||||
|
await this.requirePaymentsFeature(data.companyId);
|
||||||
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
const fiscalProfile = await this.requireActiveFiscalProfile(data.companyId, data.sessionUser);
|
||||||
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
const loginTicket = await this.getWsaaLoginTicket(data.companyId);
|
||||||
const environment = this.getArcaEnvironment();
|
const environment = this.getArcaEnvironment();
|
||||||
@@ -1054,8 +1080,11 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
|
|
||||||
await validateSessionUser({ sessionUser: data.sessionUser });
|
await validateSessionUser({ sessionUser: data.sessionUser });
|
||||||
|
|
||||||
|
let companyId: string | undefined;
|
||||||
|
|
||||||
if (data.companyId) {
|
if (data.companyId) {
|
||||||
await validatePermissionsByCompany({ companyId: data.companyId, sessionUser: data.sessionUser });
|
await validatePermissionsByCompany({ companyId: data.companyId, sessionUser: data.sessionUser });
|
||||||
|
companyId = data.companyId;
|
||||||
} else if (data.id) {
|
} else if (data.id) {
|
||||||
const credential = await this.arcaCredentials.findOne(data);
|
const credential = await this.arcaCredentials.findOne(data);
|
||||||
|
|
||||||
@@ -1067,12 +1096,13 @@ class ArcaCredentialsManager implements IArcaCredentialsManager {
|
|||||||
companyId: String(credential.companyId),
|
companyId: String(credential.companyId),
|
||||||
sessionUser: data.sessionUser,
|
sessionUser: data.sessionUser,
|
||||||
});
|
});
|
||||||
|
companyId = String(credential.companyId);
|
||||||
return [this.sanitizeCredential(credential)];
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Se requiere una organización para buscar credenciales ARCA");
|
throw new Error("Se requiere una organización para buscar credenciales ARCA");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.requirePaymentsFeature(companyId);
|
||||||
|
|
||||||
const credentials = await this.arcaCredentials.find(data);
|
const credentials = await this.arcaCredentials.find(data);
|
||||||
return credentials.map((credential) => this.sanitizeCredential(credential));
|
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);
|
const [wapError, setWapError] = useState<string | null>(null);
|
||||||
|
|
||||||
// State for ARCA onboarding flow
|
// State for ARCA onboarding flow
|
||||||
|
const [arcaIsBlocked, setArcaIsBlocked] = useState(false);
|
||||||
const [arcaCuit, setArcaCuit] = useState("");
|
const [arcaCuit, setArcaCuit] = useState("");
|
||||||
const [arcaLegalName, setArcaLegalName] = useState("");
|
const [arcaLegalName, setArcaLegalName] = useState("");
|
||||||
const [arcaTaxCondition, setArcaTaxCondition] = useState<"MONOTRIBUTO" | "EXENTO">("MONOTRIBUTO");
|
const [arcaTaxCondition, setArcaTaxCondition] = useState<"MONOTRIBUTO" | "EXENTO">("MONOTRIBUTO");
|
||||||
@@ -277,6 +278,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadOrganizationContext = (org: any) => {
|
const loadOrganizationContext = (org: any) => {
|
||||||
|
setArcaIsBlocked(false);
|
||||||
setCreatedCompanyId(org.id || org._id);
|
setCreatedCompanyId(org.id || org._id);
|
||||||
setSelectedOrganization(org);
|
setSelectedOrganization(org);
|
||||||
setOrgName(org.name || "");
|
setOrgName(org.name || "");
|
||||||
@@ -499,6 +501,18 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
|||||||
setWapQr(null);
|
setWapQr(null);
|
||||||
setWapError(null);
|
setWapError(null);
|
||||||
} else if (flowId === "arca-onboarding") {
|
} 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);
|
setCurrentStepIndex(0);
|
||||||
setArcaError(null);
|
setArcaError(null);
|
||||||
setArcaLoading(false);
|
setArcaLoading(false);
|
||||||
@@ -527,6 +541,19 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
|||||||
setArcaStatusLabel(getArcaStatusLabel(credential.status));
|
setArcaStatusLabel(getArcaStatusLabel(credential.status));
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
} else if (flowId === "arca-test-connection") {
|
} 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);
|
setCurrentStepIndex(0);
|
||||||
setArcaError(null);
|
setArcaError(null);
|
||||||
setArcaLoading(false);
|
setArcaLoading(false);
|
||||||
@@ -2415,8 +2442,42 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
|||||||
if (flowId === "public-link") return renderPublicLinkFlow();
|
if (flowId === "public-link") return renderPublicLinkFlow();
|
||||||
if (flowId === "notifications") return renderNotificationsFlow();
|
if (flowId === "notifications") return renderNotificationsFlow();
|
||||||
if (flowId === "whatsapp-bot") return renderWhatsAppBotFlow();
|
if (flowId === "whatsapp-bot") return renderWhatsAppBotFlow();
|
||||||
if (flowId === "arca-onboarding") return renderArcaOnboardingFlow();
|
if (flowId === "arca-onboarding") {
|
||||||
if (flowId === "arca-test-connection") return renderArcaTestConnectionFlow();
|
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 === "disable-schedule") return renderDisableScheduleFlow();
|
||||||
if (flowId === "open-reservation-periods") return renderReservationPeriodFlow();
|
if (flowId === "open-reservation-periods") return renderReservationPeriodFlow();
|
||||||
if (flowId === "override-schedule") return renderOverrideScheduleFlow();
|
if (flowId === "override-schedule") return renderOverrideScheduleFlow();
|
||||||
|
|||||||
Reference in New Issue
Block a user