feat: agregar validaciones de acceso a pagos y mejorar la estructura del componente de perfil fiscal
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<Location />
|
||||
<div className={style.pageContainer}>
|
||||
<section className={style.heroCard}>
|
||||
<div className={style.heroIcon}>
|
||||
<ReceiptLongOutlinedIcon />
|
||||
</div>
|
||||
<div>
|
||||
<h2>Configuración fiscal</h2>
|
||||
<p>
|
||||
Estos datos se usan para emitir Factura C desde los movimientos de caja y para precargar el asistente ARCA.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<SecureByPlan content={SECURE_CONTENTS.PAYMENTS} companyId={id}>
|
||||
<div className={style.pageContainer}>
|
||||
<section className={style.heroCard}>
|
||||
<div className={style.heroIcon}>
|
||||
<ReceiptLongOutlinedIcon />
|
||||
</div>
|
||||
<div>
|
||||
<h2>Configuración fiscal</h2>
|
||||
<p>
|
||||
Estos datos se usan para emitir Factura C desde los movimientos de caja y para precargar el asistente ARCA.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={style.formSection}>
|
||||
<h3>Datos del emisor</h3>
|
||||
<Grid2 container spacing={2}>
|
||||
<Grid2 size={{ xs: 12, sm: 6 }}>
|
||||
<Textbox
|
||||
name="cuit"
|
||||
placeholder="CUIT"
|
||||
type="text"
|
||||
value={cuit}
|
||||
width="100%"
|
||||
onChange={(event) => {
|
||||
setCuit(event.target.value.replace(/\D/g, ""));
|
||||
markNeedsSave();
|
||||
}}
|
||||
/>
|
||||
</Grid2>
|
||||
<Grid2 size={{ xs: 12, sm: 6 }}>
|
||||
<Textbox
|
||||
name="legalName"
|
||||
placeholder="Razón social"
|
||||
type="text"
|
||||
value={legalName}
|
||||
width="100%"
|
||||
onChange={(event) => {
|
||||
setLegalName(event.target.value);
|
||||
markNeedsSave();
|
||||
}}
|
||||
/>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
</section>
|
||||
<section className={style.formSection}>
|
||||
<h3>Datos del emisor</h3>
|
||||
<Grid2 container spacing={2}>
|
||||
<Grid2 size={{ xs: 12, sm: 6 }}>
|
||||
<Textbox
|
||||
name="cuit"
|
||||
placeholder="CUIT"
|
||||
type="text"
|
||||
value={cuit}
|
||||
width="100%"
|
||||
onChange={(event) => {
|
||||
setCuit(event.target.value.replace(/\D/g, ""));
|
||||
markNeedsSave();
|
||||
}}
|
||||
/>
|
||||
</Grid2>
|
||||
<Grid2 size={{ xs: 12, sm: 6 }}>
|
||||
<Textbox
|
||||
name="legalName"
|
||||
placeholder="Razón social"
|
||||
type="text"
|
||||
value={legalName}
|
||||
width="100%"
|
||||
onChange={(event) => {
|
||||
setLegalName(event.target.value);
|
||||
markNeedsSave();
|
||||
}}
|
||||
/>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
</section>
|
||||
|
||||
<section className={style.formSection}>
|
||||
<div className={style.sectionHeader}>
|
||||
<h3>Condición fiscal</h3>
|
||||
<p>Seleccioná la condición declarada para esta organización.</p>
|
||||
</div>
|
||||
<div className={style.optionGroup} role="radiogroup" aria-label="Condición fiscal">
|
||||
{fiscalConditionOptions.map((option) => {
|
||||
const selected = taxCondition === option.id;
|
||||
<section className={style.formSection}>
|
||||
<div className={style.sectionHeader}>
|
||||
<h3>Condición fiscal</h3>
|
||||
<p>Seleccioná la condición declarada para esta organización.</p>
|
||||
</div>
|
||||
<div className={style.optionGroup} role="radiogroup" aria-label="Condición fiscal">
|
||||
{fiscalConditionOptions.map((option) => {
|
||||
const selected = taxCondition === option.id;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
className={`${style.optionCard} ${selected ? style.optionCardSelected : ""}`}
|
||||
onClick={() => {
|
||||
setTaxCondition(option.id);
|
||||
markNeedsSave();
|
||||
}}
|
||||
aria-pressed={selected}
|
||||
>
|
||||
<span className={style.optionTitle}>{option.title}</span>
|
||||
<span className={style.optionDescription}>{option.description}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={style.formSection}>
|
||||
<h3>Facturación ARCA</h3>
|
||||
<Grid2 container spacing={2}>
|
||||
<Grid2 size={{ xs: 12, sm: 6 }}>
|
||||
<Textbox
|
||||
name="pointOfSale"
|
||||
placeholder="Punto de venta"
|
||||
type="text"
|
||||
value={pointOfSale}
|
||||
width="100%"
|
||||
onChange={(event) => {
|
||||
setPointOfSale(event.target.value.replace(/\D/g, ""));
|
||||
markNeedsSave();
|
||||
}}
|
||||
/>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
|
||||
{fiscalDataLoaded && (
|
||||
<TextObjectFilter
|
||||
key={`${activityCode}-${arcaActivities.length}`}
|
||||
placeholder="Actividad ARCA/CLAE"
|
||||
content={activityFilterContent}
|
||||
data={arcaActivities}
|
||||
selectedId={activityCode}
|
||||
onChange={(selectedActivityCode) => {
|
||||
const selectedActivity = arcaActivities.find(
|
||||
(activity) => activity.id === selectedActivityCode
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
className={`${style.optionCard} ${selected ? style.optionCardSelected : ""}`}
|
||||
onClick={() => {
|
||||
setTaxCondition(option.id);
|
||||
markNeedsSave();
|
||||
}}
|
||||
aria-pressed={selected}
|
||||
>
|
||||
<span className={style.optionTitle}>{option.title}</span>
|
||||
<span className={style.optionDescription}>{option.description}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
setActivityCode(selectedActivity?.id || "");
|
||||
setActivityDescription(selectedActivity?.name || "");
|
||||
markNeedsSave();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<section className={style.formSection}>
|
||||
<h3>Facturación ARCA</h3>
|
||||
<Grid2 container spacing={2}>
|
||||
<Grid2 size={{ xs: 12, sm: 6 }}>
|
||||
<Textbox
|
||||
name="pointOfSale"
|
||||
placeholder="Punto de venta"
|
||||
type="text"
|
||||
value={pointOfSale}
|
||||
width="100%"
|
||||
onChange={(event) => {
|
||||
setPointOfSale(event.target.value.replace(/\D/g, ""));
|
||||
markNeedsSave();
|
||||
}}
|
||||
/>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
|
||||
<p className={style.helpText}>
|
||||
{activityCode && activityDescription
|
||||
? `${activityCode} - ${activityDescription}`
|
||||
: "Seleccioná la actividad principal declarada en ARCA."}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
{fiscalDataLoaded && (
|
||||
<TextObjectFilter
|
||||
key={`${activityCode}-${arcaActivities.length}`}
|
||||
placeholder="Actividad ARCA/CLAE"
|
||||
content={activityFilterContent}
|
||||
data={arcaActivities}
|
||||
selectedId={activityCode}
|
||||
onChange={(selectedActivityCode) => {
|
||||
const selectedActivity = arcaActivities.find(
|
||||
(activity) => activity.id === selectedActivityCode
|
||||
);
|
||||
|
||||
setActivityCode(selectedActivity?.id || "");
|
||||
setActivityDescription(selectedActivity?.name || "");
|
||||
markNeedsSave();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<p className={style.helpText}>
|
||||
{activityCode && activityDescription
|
||||
? `${activityCode} - ${activityDescription}`
|
||||
: "Seleccioná la actividad principal declarada en ARCA."}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</SecureByPlan>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user