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 { 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 { findArcaActivityByCode } from "../ArcaActivities/ArcaActivities.Catalog";
|
||||||
import { OrganizationFiscalProfilesAdapterMongoose } from "./OrganizationFiscalProfiles.Adapter.Mongoose";
|
import { OrganizationFiscalProfilesAdapterMongoose } from "./OrganizationFiscalProfiles.Adapter.Mongoose";
|
||||||
import {
|
import {
|
||||||
@@ -70,6 +73,21 @@ class OrganizationFiscalProfilesManager implements IOrganizationFiscalProfilesMa
|
|||||||
|
|
||||||
await validatePermissionsByCompany({ companyId: data.companyId, sessionUser: data.sessionUser });
|
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);
|
this.validateProfileFields(data);
|
||||||
|
|
||||||
const existingProfile = await this.organizationFiscalProfiles.findOne({
|
const existingProfile = await this.organizationFiscalProfiles.findOne({
|
||||||
@@ -140,6 +158,18 @@ class OrganizationFiscalProfilesManager implements IOrganizationFiscalProfilesMa
|
|||||||
companyId: data.companyId,
|
companyId: data.companyId,
|
||||||
sessionUser,
|
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) {
|
} else if (data.id) {
|
||||||
const profile = await this.organizationFiscalProfiles.findOne(data);
|
const profile = await this.organizationFiscalProfiles.findOne(data);
|
||||||
|
|
||||||
@@ -152,6 +182,18 @@ class OrganizationFiscalProfilesManager implements IOrganizationFiscalProfilesMa
|
|||||||
sessionUser,
|
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];
|
return [profile];
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Se requiere una organización para buscar perfiles fiscales");
|
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 { Grid2 } from "@mui/material";
|
||||||
import { ApiError } from "@models/Server.Error.model";
|
import { ApiError } from "@models/Server.Error.model";
|
||||||
import style from "./page.module.css";
|
import style from "./page.module.css";
|
||||||
|
import SecureByPlan, { SECURE_CONTENTS } from "@core/app/components/SecureByPlan/SecureByPlan";
|
||||||
import {
|
import {
|
||||||
findArcaActivities,
|
findArcaActivities,
|
||||||
findOrganizationFiscalProfiles,
|
findOrganizationFiscalProfiles,
|
||||||
@@ -188,123 +189,125 @@ export default function OrganizationFiscalProfileEdit() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Location />
|
<Location />
|
||||||
<div className={style.pageContainer}>
|
<SecureByPlan content={SECURE_CONTENTS.PAYMENTS} companyId={id}>
|
||||||
<section className={style.heroCard}>
|
<div className={style.pageContainer}>
|
||||||
<div className={style.heroIcon}>
|
<section className={style.heroCard}>
|
||||||
<ReceiptLongOutlinedIcon />
|
<div className={style.heroIcon}>
|
||||||
</div>
|
<ReceiptLongOutlinedIcon />
|
||||||
<div>
|
</div>
|
||||||
<h2>Configuración fiscal</h2>
|
<div>
|
||||||
<p>
|
<h2>Configuración fiscal</h2>
|
||||||
Estos datos se usan para emitir Factura C desde los movimientos de caja y para precargar el asistente ARCA.
|
<p>
|
||||||
</p>
|
Estos datos se usan para emitir Factura C desde los movimientos de caja y para precargar el asistente ARCA.
|
||||||
</div>
|
</p>
|
||||||
</section>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section className={style.formSection}>
|
<section className={style.formSection}>
|
||||||
<h3>Datos del emisor</h3>
|
<h3>Datos del emisor</h3>
|
||||||
<Grid2 container spacing={2}>
|
<Grid2 container spacing={2}>
|
||||||
<Grid2 size={{ xs: 12, sm: 6 }}>
|
<Grid2 size={{ xs: 12, sm: 6 }}>
|
||||||
<Textbox
|
<Textbox
|
||||||
name="cuit"
|
name="cuit"
|
||||||
placeholder="CUIT"
|
placeholder="CUIT"
|
||||||
type="text"
|
type="text"
|
||||||
value={cuit}
|
value={cuit}
|
||||||
width="100%"
|
width="100%"
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setCuit(event.target.value.replace(/\D/g, ""));
|
setCuit(event.target.value.replace(/\D/g, ""));
|
||||||
markNeedsSave();
|
markNeedsSave();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<Grid2 size={{ xs: 12, sm: 6 }}>
|
<Grid2 size={{ xs: 12, sm: 6 }}>
|
||||||
<Textbox
|
<Textbox
|
||||||
name="legalName"
|
name="legalName"
|
||||||
placeholder="Razón social"
|
placeholder="Razón social"
|
||||||
type="text"
|
type="text"
|
||||||
value={legalName}
|
value={legalName}
|
||||||
width="100%"
|
width="100%"
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setLegalName(event.target.value);
|
setLegalName(event.target.value);
|
||||||
markNeedsSave();
|
markNeedsSave();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className={style.formSection}>
|
<section className={style.formSection}>
|
||||||
<div className={style.sectionHeader}>
|
<div className={style.sectionHeader}>
|
||||||
<h3>Condición fiscal</h3>
|
<h3>Condición fiscal</h3>
|
||||||
<p>Seleccioná la condición declarada para esta organización.</p>
|
<p>Seleccioná la condición declarada para esta organización.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={style.optionGroup} role="radiogroup" aria-label="Condición fiscal">
|
<div className={style.optionGroup} role="radiogroup" aria-label="Condición fiscal">
|
||||||
{fiscalConditionOptions.map((option) => {
|
{fiscalConditionOptions.map((option) => {
|
||||||
const selected = taxCondition === option.id;
|
const selected = taxCondition === option.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={option.id}
|
key={option.id}
|
||||||
type="button"
|
type="button"
|
||||||
className={`${style.optionCard} ${selected ? style.optionCardSelected : ""}`}
|
className={`${style.optionCard} ${selected ? style.optionCardSelected : ""}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTaxCondition(option.id);
|
setTaxCondition(option.id);
|
||||||
markNeedsSave();
|
markNeedsSave();
|
||||||
}}
|
}}
|
||||||
aria-pressed={selected}
|
aria-pressed={selected}
|
||||||
>
|
>
|
||||||
<span className={style.optionTitle}>{option.title}</span>
|
<span className={style.optionTitle}>{option.title}</span>
|
||||||
<span className={style.optionDescription}>{option.description}</span>
|
<span className={style.optionDescription}>{option.description}</span>
|
||||||
</button>
|
</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
|
|
||||||
);
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
setActivityCode(selectedActivity?.id || "");
|
<section className={style.formSection}>
|
||||||
setActivityDescription(selectedActivity?.name || "");
|
<h3>Facturación ARCA</h3>
|
||||||
markNeedsSave();
|
<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}>
|
{fiscalDataLoaded && (
|
||||||
{activityCode && activityDescription
|
<TextObjectFilter
|
||||||
? `${activityCode} - ${activityDescription}`
|
key={`${activityCode}-${arcaActivities.length}`}
|
||||||
: "Seleccioná la actividad principal declarada en ARCA."}
|
placeholder="Actividad ARCA/CLAE"
|
||||||
</p>
|
content={activityFilterContent}
|
||||||
</section>
|
data={arcaActivities}
|
||||||
</div>
|
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