v.0.8.1
This commit is contained in:
parent
7a161717c2
commit
6187a77dd9
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/factuges-server",
|
"name": "@erp/factuges-server",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsup src/index.ts --config tsup.config.ts",
|
"build": "tsup src/index.ts --config tsup.config.ts",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/factuges-web",
|
"name": "@erp/factuges-web",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/auth",
|
"name": "@erp/auth",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/catalogs",
|
"name": "@erp/catalogs",
|
||||||
"description": "Catalogs module",
|
"description": "Catalogs module",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/core",
|
"name": "@erp/core",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/customer-invoices",
|
"name": "@erp/customer-invoices",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
InvoicePaymentMethod,
|
InvoicePaymentMethod,
|
||||||
InvoiceStatus,
|
InvoiceStatus,
|
||||||
IssuedInvoiceItem,
|
IssuedInvoiceItem,
|
||||||
|
IssuedInvoiceRecipient,
|
||||||
IssuedInvoiceTax,
|
IssuedInvoiceTax,
|
||||||
IssuedInvoiceTaxes,
|
IssuedInvoiceTaxes,
|
||||||
type Proforma,
|
type Proforma,
|
||||||
@ -29,6 +30,11 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic
|
|||||||
public toCreateProps(source: ProformaIssueReadModel): Result<IIssuedInvoiceCreateProps, Error> {
|
public toCreateProps(source: ProformaIssueReadModel): Result<IIssuedInvoiceCreateProps, Error> {
|
||||||
const { proforma } = source;
|
const { proforma } = source;
|
||||||
|
|
||||||
|
const recipientResult = this.resolveRecipient(proforma);
|
||||||
|
if (recipientResult.isFailure) {
|
||||||
|
return Result.fail(recipientResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
const itemsResult = this.resolveItems(proforma);
|
const itemsResult = this.resolveItems(proforma);
|
||||||
|
|
||||||
if (itemsResult.isFailure) {
|
if (itemsResult.isFailure) {
|
||||||
@ -72,7 +78,7 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic
|
|||||||
paymentMethod: paymentResult.data,
|
paymentMethod: paymentResult.data,
|
||||||
|
|
||||||
customerId: proforma.customerId,
|
customerId: proforma.customerId,
|
||||||
recipient: proforma.recipient.getOrUndefined()!,
|
recipient: recipientResult.data,
|
||||||
|
|
||||||
items: itemsResult.data,
|
items: itemsResult.data,
|
||||||
|
|
||||||
@ -99,7 +105,31 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic
|
|||||||
totalAmount: proformaTotals.totalAmount,
|
totalAmount: proformaTotals.totalAmount,
|
||||||
|
|
||||||
verifactu: Maybe.none(),
|
verifactu: Maybe.none(),
|
||||||
|
} satisfies IIssuedInvoiceCreateProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveRecipient(proforma: Proforma): Result<IssuedInvoiceRecipient, Error> {
|
||||||
|
const recipient = proforma.recipient;
|
||||||
|
|
||||||
|
if (recipient.isNone()) {
|
||||||
|
return Result.fail(new Error("Recipient is required"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipientValue = recipient.unwrap();
|
||||||
|
|
||||||
|
const newRecipient = IssuedInvoiceRecipient.create({
|
||||||
|
name: recipientValue.name,
|
||||||
|
tin: recipientValue.tin.unwrap(),
|
||||||
|
|
||||||
|
street: recipientValue.street.unwrap(),
|
||||||
|
street2: recipientValue.street2,
|
||||||
|
city: recipientValue.city.unwrap(),
|
||||||
|
postalCode: recipientValue.postalCode.unwrap(),
|
||||||
|
province: recipientValue.province,
|
||||||
|
country: recipientValue.country,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return newRecipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveItems(proforma: Proforma): Result<IssuedInvoiceItem[], Error> {
|
private resolveItems(proforma: Proforma): Result<IssuedInvoiceItem[], Error> {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
import { DomainValidationError, maybeToEmptyString, maybeToNullable } from "@repo/rdx-ddd";
|
import { DomainValidationError, maybeToNullable } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
import type { IssuedInvoice } from "../../../../domain";
|
import type { IssuedInvoice } from "../../../../domain";
|
||||||
|
|
||||||
@ -22,12 +22,12 @@ export class IssuedInvoiceRecipientFullSnapshotBuilder
|
|||||||
return {
|
return {
|
||||||
id: invoice.customerId.toString(),
|
id: invoice.customerId.toString(),
|
||||||
name: recipient.name.toString(),
|
name: recipient.name.toString(),
|
||||||
tin: maybeToEmptyString(recipient.tin, (v) => v.toString()),
|
tin: recipient.tin.toString(),
|
||||||
street: maybeToNullable(recipient.street, (v) => v.toString()),
|
street: recipient.street.toString(),
|
||||||
street2: maybeToNullable(recipient.street2, (v) => v.toString()),
|
street2: maybeToNullable(recipient.street2, (v) => v.toString()),
|
||||||
city: maybeToNullable(recipient.city, (v) => v.toString()),
|
city: recipient.city.toString(),
|
||||||
province: maybeToNullable(recipient.province, (v) => v.toString()),
|
province: maybeToNullable(recipient.province, (v) => v.toString()),
|
||||||
postal_code: maybeToNullable(recipient.postalCode, (v) => v.toString()),
|
postal_code: recipient.postalCode.toString(),
|
||||||
country: maybeToNullable(recipient.country, (v) => v.toString()),
|
country: maybeToNullable(recipient.country, (v) => v.toString()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,10 +14,10 @@ import { type Maybe, Result } from "@repo/rdx-utils";
|
|||||||
export type IssuedInvoiceRecipientProps = {
|
export type IssuedInvoiceRecipientProps = {
|
||||||
name: Name;
|
name: Name;
|
||||||
tin: TINNumber;
|
tin: TINNumber;
|
||||||
street: Maybe<Street>;
|
street: Street;
|
||||||
street2: Maybe<Street>;
|
street2: Maybe<Street>;
|
||||||
city: Maybe<City>;
|
city: City;
|
||||||
postalCode: Maybe<PostalCode>;
|
postalCode: PostalCode;
|
||||||
province: Maybe<Province>;
|
province: Maybe<Province>;
|
||||||
country: Maybe<Country>;
|
country: Maybe<Country>;
|
||||||
};
|
};
|
||||||
@ -56,7 +56,7 @@ export class IssuedInvoiceRecipient extends ValueObject<IssuedInvoiceRecipientPr
|
|||||||
return this.props.tin;
|
return this.props.tin;
|
||||||
}
|
}
|
||||||
|
|
||||||
get street(): Maybe<Street> {
|
get street(): Street {
|
||||||
return this.props.street;
|
return this.props.street;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,11 +64,11 @@ export class IssuedInvoiceRecipient extends ValueObject<IssuedInvoiceRecipientPr
|
|||||||
return this.props.street2;
|
return this.props.street2;
|
||||||
}
|
}
|
||||||
|
|
||||||
get city(): Maybe<City> {
|
get city(): City {
|
||||||
return this.props.city;
|
return this.props.city;
|
||||||
}
|
}
|
||||||
|
|
||||||
get postalCode(): Maybe<PostalCode> {
|
get postalCode(): PostalCode {
|
||||||
return this.props.postalCode;
|
return this.props.postalCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,10 +92,10 @@ export class IssuedInvoiceRecipient extends ValueObject<IssuedInvoiceRecipientPr
|
|||||||
return {
|
return {
|
||||||
tin: this.tin.toString(),
|
tin: this.tin.toString(),
|
||||||
name: this.name.toString(),
|
name: this.name.toString(),
|
||||||
street: maybeToNullable(this.street, (value) => value.toString()),
|
street: this.street.toString(),
|
||||||
street2: maybeToNullable(this.street2, (value) => value.toString()),
|
street2: maybeToNullable(this.street2, (value) => value.toString()),
|
||||||
city: maybeToNullable(this.city, (value) => value.toString()),
|
city: this.city.toString(),
|
||||||
postal_code: maybeToNullable(this.postalCode, (value) => value.toString()),
|
postal_code: this.postalCode.toString(),
|
||||||
province: maybeToNullable(this.province, (value) => value.toString()),
|
province: maybeToNullable(this.province, (value) => value.toString()),
|
||||||
country: maybeToNullable(this.country, (value) => value.toString()),
|
country: maybeToNullable(this.country, (value) => value.toString()),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -314,7 +314,7 @@ export class Proforma extends AggregateRoot<ProformaInternalProps> implements IP
|
|||||||
return Result.ok(false);
|
return Result.ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canManuallyTransitionProformaStatus({ currentStatus, nextStatus })) {
|
if (!canManuallyTransitionProformaStatus({ currentStatus, nextStatus, proforma: this })) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new InvalidProformaTransitionError(
|
new InvalidProformaTransitionError(
|
||||||
currentStatus.toPrimitive(),
|
currentStatus.toPrimitive(),
|
||||||
@ -392,6 +392,26 @@ export class Proforma extends AggregateRoot<ProformaInternalProps> implements IP
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const recipientValue = this.recipient.unwrap();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!recipientValue.name ||
|
||||||
|
recipientValue.tin.isNone() ||
|
||||||
|
recipientValue.street.isNone() ||
|
||||||
|
recipientValue.city.isNone() ||
|
||||||
|
recipientValue.postalCode.isNone() ||
|
||||||
|
recipientValue.province.isNone() ||
|
||||||
|
recipientValue.country.isNone()
|
||||||
|
) {
|
||||||
|
return Result.fail(
|
||||||
|
new DomainValidationError(
|
||||||
|
"MISSING_RECIPIENT",
|
||||||
|
"recipient",
|
||||||
|
"Recipient is required to issue the proforma"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.items.size() === 0) {
|
if (this.items.size() === 0) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new DomainValidationError(
|
new DomainValidationError(
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { INVOICE_STATUS, type InvoiceStatus } from "../..";
|
import { INVOICE_STATUS, type InvoiceStatus, type Proforma } from "../..";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transiciones manuales permitidas para una proforma.
|
* Transiciones manuales permitidas para una proforma.
|
||||||
@ -17,11 +17,38 @@ const PROFORMA_MANUAL_STATUS_TRANSITIONS: Readonly<
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function canManuallyTransitionProformaStatus(params: {
|
export function canManuallyTransitionProformaStatus(params: {
|
||||||
|
proforma: Proforma;
|
||||||
currentStatus: InvoiceStatus;
|
currentStatus: InvoiceStatus;
|
||||||
nextStatus: InvoiceStatus;
|
nextStatus: InvoiceStatus;
|
||||||
}): boolean {
|
}): boolean {
|
||||||
const current = params.currentStatus.toPrimitive() as INVOICE_STATUS;
|
const current = params.currentStatus.toPrimitive() as INVOICE_STATUS;
|
||||||
const next = params.nextStatus.toPrimitive() as INVOICE_STATUS;
|
const next = params.nextStatus.toPrimitive() as INVOICE_STATUS;
|
||||||
|
|
||||||
return PROFORMA_MANUAL_STATUS_TRANSITIONS[current].includes(next);
|
let possible = PROFORMA_MANUAL_STATUS_TRANSITIONS[current].includes(next);
|
||||||
|
|
||||||
|
// Validación adicional: si la transición es diferente a 'draft', debe existir
|
||||||
|
// un destinatario obligatoriamente con todos sus datos rellenos.
|
||||||
|
|
||||||
|
if (next !== INVOICE_STATUS.DRAFT) {
|
||||||
|
const { recipient } = params.proforma;
|
||||||
|
if (recipient.isNone()) {
|
||||||
|
possible = false;
|
||||||
|
} else {
|
||||||
|
const recipientValue = recipient.unwrap();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!recipientValue.name ||
|
||||||
|
recipientValue.tin.isNone() ||
|
||||||
|
recipientValue.street.isNone() ||
|
||||||
|
recipientValue.city.isNone() ||
|
||||||
|
recipientValue.postalCode.isNone() ||
|
||||||
|
recipientValue.province.isNone() ||
|
||||||
|
recipientValue.country.isNone()
|
||||||
|
) {
|
||||||
|
possible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return possible;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,11 +47,7 @@ export class SequelizeIssuedInvoiceRecipientDomainMapper {
|
|||||||
//const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
|
//const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
|
||||||
const customerTin = extractOrPushError(TINNumber.create(_tin), "customer_tin", errors);
|
const customerTin = extractOrPushError(TINNumber.create(_tin), "customer_tin", errors);
|
||||||
|
|
||||||
const customerStreet = extractOrPushError(
|
const customerStreet = extractOrPushError(Street.create(_street), "customer_street", errors);
|
||||||
maybeFromNullableResult(_street, (value) => Street.create(value)),
|
|
||||||
"customer_street",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const customerStreet2 = extractOrPushError(
|
const customerStreet2 = extractOrPushError(
|
||||||
maybeFromNullableResult(_street2, (value) => Street.create(value)),
|
maybeFromNullableResult(_street2, (value) => Street.create(value)),
|
||||||
@ -59,11 +55,7 @@ export class SequelizeIssuedInvoiceRecipientDomainMapper {
|
|||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const customerCity = extractOrPushError(
|
const customerCity = extractOrPushError(City.create(_city), "customer_city", errors);
|
||||||
maybeFromNullableResult(_city, (value) => City.create(value)),
|
|
||||||
"customer_city",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const customerProvince = extractOrPushError(
|
const customerProvince = extractOrPushError(
|
||||||
maybeFromNullableResult(_province, (value) => Province.create(value)),
|
maybeFromNullableResult(_province, (value) => Province.create(value)),
|
||||||
@ -72,7 +64,7 @@ export class SequelizeIssuedInvoiceRecipientDomainMapper {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const customerPostalCode = extractOrPushError(
|
const customerPostalCode = extractOrPushError(
|
||||||
maybeFromNullableResult(_postal_code, (value) => PostalCode.create(value)),
|
PostalCode.create(_postal_code),
|
||||||
"customer_postal_code",
|
"customer_postal_code",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
@ -125,11 +117,11 @@ export class SequelizeIssuedInvoiceRecipientDomainMapper {
|
|||||||
return {
|
return {
|
||||||
customer_tin: recipient.tin.toString(),
|
customer_tin: recipient.tin.toString(),
|
||||||
customer_name: recipient.name.toPrimitive(),
|
customer_name: recipient.name.toPrimitive(),
|
||||||
customer_street: maybeToNullable(recipient.street, (v) => v.toPrimitive()),
|
customer_street: recipient.street.toPrimitive(),
|
||||||
customer_street2: maybeToNullable(recipient.street2, (v) => v.toPrimitive()),
|
customer_street2: maybeToNullable(recipient.street2, (v) => v.toPrimitive()),
|
||||||
customer_city: maybeToNullable(recipient.city, (v) => v.toPrimitive()),
|
customer_city: recipient.city.toPrimitive(),
|
||||||
customer_province: maybeToNullable(recipient.province, (v) => v.toPrimitive()),
|
customer_province: maybeToNullable(recipient.province, (v) => v.toPrimitive()),
|
||||||
customer_postal_code: maybeToNullable(recipient.postalCode, (v) => v.toPrimitive()),
|
customer_postal_code: recipient.postalCode.toPrimitive(),
|
||||||
customer_country: maybeToNullable(recipient.country, (v) => v.toPrimitive()),
|
customer_country: maybeToNullable(recipient.country, (v) => v.toPrimitive()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,11 +58,7 @@ export class SequelizeIssuedInvoiceRecipientListMapper extends SequelizeQueryMap
|
|||||||
|
|
||||||
const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
|
const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
|
||||||
|
|
||||||
const customerStreet = extractOrPushError(
|
const customerStreet = extractOrPushError(Street.create(_street), "customer_street", errors);
|
||||||
maybeFromNullableResult(_street, (value) => Street.create(value)),
|
|
||||||
"customer_street",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const customerStreet2 = extractOrPushError(
|
const customerStreet2 = extractOrPushError(
|
||||||
maybeFromNullableResult(_street2, (value) => Street.create(value)),
|
maybeFromNullableResult(_street2, (value) => Street.create(value)),
|
||||||
@ -70,11 +66,7 @@ export class SequelizeIssuedInvoiceRecipientListMapper extends SequelizeQueryMap
|
|||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const customerCity = extractOrPushError(
|
const customerCity = extractOrPushError(City.create(_city), "customer_city", errors);
|
||||||
maybeFromNullableResult(_city, (value) => City.create(value)),
|
|
||||||
"customer_city",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const customerProvince = extractOrPushError(
|
const customerProvince = extractOrPushError(
|
||||||
maybeFromNullableResult(_province, (value) => Province.create(value)),
|
maybeFromNullableResult(_province, (value) => Province.create(value)),
|
||||||
@ -83,7 +75,7 @@ export class SequelizeIssuedInvoiceRecipientListMapper extends SequelizeQueryMap
|
|||||||
);
|
);
|
||||||
|
|
||||||
const customerPostalCode = extractOrPushError(
|
const customerPostalCode = extractOrPushError(
|
||||||
maybeFromNullableResult(_postal_code, (value) => PostalCode.create(value)),
|
PostalCode.create(_postal_code),
|
||||||
"customer_postal_code",
|
"customer_postal_code",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|||||||
@ -259,9 +259,8 @@ export class IssuedInvoiceRepository
|
|||||||
column: "description",
|
column: "description",
|
||||||
},
|
},
|
||||||
recipient_name: {
|
recipient_name: {
|
||||||
type: "association",
|
type: "root",
|
||||||
association: "current_customer",
|
column: "recipient_name",
|
||||||
column: "name",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sortableFields: [
|
sortableFields: [
|
||||||
@ -272,6 +271,7 @@ export class IssuedInvoiceRepository
|
|||||||
"created_at",
|
"created_at",
|
||||||
"serie",
|
"serie",
|
||||||
],
|
],
|
||||||
|
fullTextTableAlias: "CustomerInvoiceModel",
|
||||||
enableFullText: true,
|
enableFullText: true,
|
||||||
database: this.database,
|
database: this.database,
|
||||||
strictMode: true, // fuerza error si ORDER BY no permitido
|
strictMode: true, // fuerza error si ORDER BY no permitido
|
||||||
@ -280,9 +280,9 @@ export class IssuedInvoiceRepository
|
|||||||
/**
|
/**
|
||||||
* Restricciones propias del repositorio.
|
* Restricciones propias del repositorio.
|
||||||
*
|
*
|
||||||
* El dominio `Proforma` no conoce `is_proforma`; este discriminador
|
* El dominio `IssuedInvoice` no conoce `is_proforma`; este discriminador
|
||||||
* pertenece a la infraestructura porque actualmente se comparte tabla
|
* pertenece a la infraestructura porque actualmente se comparte tabla
|
||||||
* con facturas emitidas.
|
* con proformas.
|
||||||
*
|
*
|
||||||
* No se añade `deleted_at: null` porque el modelo usa `paranoid: true`;
|
* No se añade `deleted_at: null` porque el modelo usa `paranoid: true`;
|
||||||
* Sequelize aplica automáticamente `deleted_at IS NULL` en las consultas.
|
* Sequelize aplica automáticamente `deleted_at IS NULL` en las consultas.
|
||||||
|
|||||||
@ -455,6 +455,7 @@ export class ProformaRepository
|
|||||||
"created_at",
|
"created_at",
|
||||||
"serie",
|
"serie",
|
||||||
],
|
],
|
||||||
|
fullTextTableAlias: "CustomerInvoiceModel",
|
||||||
enableFullText: true,
|
enableFullText: true,
|
||||||
database: this.database,
|
database: this.database,
|
||||||
strictMode: true, // fuerza error si ORDER BY no permitido
|
strictMode: true, // fuerza error si ORDER BY no permitido
|
||||||
|
|||||||
@ -89,7 +89,7 @@ export const ProformaLineEditor = ({
|
|||||||
{
|
{
|
||||||
id: "quantity",
|
id: "quantity",
|
||||||
header: t("form_fields.items.quantity.label", "Cantidad"),
|
header: t("form_fields.items.quantity.label", "Cantidad"),
|
||||||
headClassName: "w-[100px] text-right",
|
headClassName: "w-[75px] text-right",
|
||||||
cell: ({ index }) => (
|
cell: ({ index }) => (
|
||||||
<QuantityField
|
<QuantityField
|
||||||
inputClassName="border-none"
|
inputClassName="border-none"
|
||||||
@ -102,7 +102,7 @@ export const ProformaLineEditor = ({
|
|||||||
{
|
{
|
||||||
id: "unitAmount",
|
id: "unitAmount",
|
||||||
header: t("form_fields.items.unit_amount.label", "Importe unitario"),
|
header: t("form_fields.items.unit_amount.label", "Importe unitario"),
|
||||||
headClassName: "w-[200px] text-right",
|
headClassName: "w-[100px] text-right",
|
||||||
cell: ({ index }) => (
|
cell: ({ index }) => (
|
||||||
<AmountField
|
<AmountField
|
||||||
inputClassName="border-none"
|
inputClassName="border-none"
|
||||||
|
|||||||
@ -95,7 +95,7 @@ export const ProformaUpdateHeaderEditor = ({
|
|||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
/>
|
/>
|
||||||
<TextAreaField
|
<TextAreaField
|
||||||
className="md:col-span-12 hidden"
|
className="md:col-span-12"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label={t("form_fields.proformas.notes.label")}
|
label={t("form_fields.proformas.notes.label")}
|
||||||
maxLength={256}
|
maxLength={256}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/customers",
|
"name": "@erp/customers",
|
||||||
"description": "Customers",
|
"description": "Customers",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -14,6 +14,11 @@ export interface ICustomerPublicServices {
|
|||||||
context: ICustomerServicesContext
|
context: ICustomerServicesContext
|
||||||
) => Promise<Result<Customer, Error>>;
|
) => Promise<Result<Customer, Error>>;
|
||||||
|
|
||||||
|
getCustomerById: (
|
||||||
|
id: UniqueID,
|
||||||
|
context: ICustomerServicesContext
|
||||||
|
) => Promise<Result<Customer, Error>>;
|
||||||
|
|
||||||
createCustomer: (
|
createCustomer: (
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
props: ICustomerCreateProps,
|
props: ICustomerCreateProps,
|
||||||
|
|||||||
@ -42,6 +42,18 @@ export function buildCustomerPublicServices(
|
|||||||
return Result.ok(customerResult.data);
|
return Result.ok(customerResult.data);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getCustomerById: async (id: UniqueID, context: ICustomerServicesContext) => {
|
||||||
|
const { companyId, transaction } = context;
|
||||||
|
|
||||||
|
const customerResult = await finder.findCustomerById(companyId, id, transaction);
|
||||||
|
|
||||||
|
if (customerResult.isFailure) {
|
||||||
|
return Result.fail(customerResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(customerResult.data);
|
||||||
|
},
|
||||||
|
|
||||||
createCustomer: async (
|
createCustomer: async (
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
props: ICustomerCreateProps,
|
props: ICustomerCreateProps,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useUrlParamId } from "@erp/core/hooks";
|
import { useUrlParamId } from "@erp/core/hooks";
|
||||||
import { useSearchParams } from "react-router-dom";
|
import { createSearchParams, useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
import { useCustomerUpdateController } from "./use-customer-update.controller";
|
import { useCustomerUpdateController } from "./use-customer-update.controller";
|
||||||
|
|
||||||
@ -11,12 +11,21 @@ import { useCustomerUpdateController } from "./use-customer-update.controller";
|
|||||||
|
|
||||||
export const useCustomerUpdatePageController = () => {
|
export const useCustomerUpdatePageController = () => {
|
||||||
const customerId = useUrlParamId();
|
const customerId = useUrlParamId();
|
||||||
|
const navigate = useNavigate();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
const updateCtrl = useCustomerUpdateController(customerId);
|
|
||||||
|
|
||||||
const returnTo = searchParams.get("returnTo") ?? "/customers";
|
const returnTo = searchParams.get("returnTo") ?? "/customers";
|
||||||
|
|
||||||
|
const updateCtrl = useCustomerUpdateController(customerId, {
|
||||||
|
onUpdated: (updated) => {
|
||||||
|
navigate({
|
||||||
|
pathname: `/customers/${updated.id}/`,
|
||||||
|
search: createSearchParams({
|
||||||
|
returnTo,
|
||||||
|
}).toString(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updateCtrl,
|
updateCtrl,
|
||||||
returnTo,
|
returnTo,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/factuges",
|
"name": "@erp/factuges",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/identity",
|
"name": "@erp/identity",
|
||||||
"description": "Identity module",
|
"description": "Identity module",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export const identityAPIModule: IModuleServer = {
|
|||||||
* - NO conecta infraestructura
|
* - NO conecta infraestructura
|
||||||
*/
|
*/
|
||||||
async setup(params) {
|
async setup(params) {
|
||||||
const { env: ENV, app, database, baseRoutePath: API_BASE_PATH, logger } = params;
|
const { logger } = params;
|
||||||
|
|
||||||
// 1) Dominio interno
|
// 1) Dominio interno
|
||||||
const internal = buildIdentityDependencies(params);
|
const internal = buildIdentityDependencies(params);
|
||||||
|
|||||||
@ -0,0 +1,107 @@
|
|||||||
|
import {
|
||||||
|
ExpressController,
|
||||||
|
type RequestWithAuth,
|
||||||
|
UnauthorizedApiError,
|
||||||
|
ValidationApiError,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
import { EmailAddress, UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import type { NextFunction, Request, Response } from "express";
|
||||||
|
|
||||||
|
import type { IAccessTokenVerifier, IAccountRepository } from "../../../application";
|
||||||
|
|
||||||
|
const COMPANY_HEADER_NAME = "X-Company-Id";
|
||||||
|
|
||||||
|
function parseBearerToken(authorization?: string): string | undefined {
|
||||||
|
if (!authorization) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [scheme, token] = authorization.split(" ");
|
||||||
|
if (scheme?.toLowerCase() !== "bearer") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return token?.trim() || undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCompanyIdFromHeader(req: Request): UniqueID | undefined | Error {
|
||||||
|
const rawCompanyId = req.get(COMPANY_HEADER_NAME);
|
||||||
|
if (!rawCompanyId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const companyIdResult = UniqueID.create(rawCompanyId);
|
||||||
|
if (companyIdResult.isFailure) {
|
||||||
|
return new Error(`Header "${COMPANY_HEADER_NAME}" must be a valid UUID.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return companyIdResult.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function authenticateUser(params: {
|
||||||
|
accessTokenVerifier: IAccessTokenVerifier;
|
||||||
|
accountRepository: IAccountRepository;
|
||||||
|
}) {
|
||||||
|
return async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const authorization = req.get("authorization");
|
||||||
|
const token = parseBearerToken(authorization);
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifiedTokenResult = await params.accessTokenVerifier.verify(token);
|
||||||
|
if (verifiedTokenResult.isFailure) {
|
||||||
|
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountIdResult = UniqueID.create(verifiedTokenResult.data.accountId);
|
||||||
|
if (accountIdResult.isFailure) {
|
||||||
|
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountResult = await params.accountRepository.findById(accountIdResult.data);
|
||||||
|
if (accountResult.isFailure || accountResult.data.isNone()) {
|
||||||
|
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const account = accountResult.data.unwrap();
|
||||||
|
const canAuthenticateResult = account.canAuthenticate();
|
||||||
|
if (canAuthenticateResult.isFailure) {
|
||||||
|
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailResult = EmailAddress.create(verifiedTokenResult.data.email);
|
||||||
|
if (emailResult.isFailure) {
|
||||||
|
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const companyIdFromHeader = getCompanyIdFromHeader(req);
|
||||||
|
if (companyIdFromHeader instanceof Error) {
|
||||||
|
return ExpressController.errorResponse(
|
||||||
|
new ValidationApiError(companyIdFromHeader.message, [
|
||||||
|
{
|
||||||
|
field: COMPANY_HEADER_NAME,
|
||||||
|
message: companyIdFromHeader.message,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
req,
|
||||||
|
res
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
(req as RequestWithAuth).user = {
|
||||||
|
userId: account.id,
|
||||||
|
email: emailResult.data,
|
||||||
|
companyId: companyIdFromHeader,
|
||||||
|
roles: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
return next();
|
||||||
|
} catch {
|
||||||
|
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,69 +1,9 @@
|
|||||||
import { ExpressController, type RequestWithAuth, UnauthorizedApiError } from "@erp/core/api";
|
|
||||||
import { EmailAddress, UniqueID } from "@repo/rdx-ddd";
|
|
||||||
import type { NextFunction, Request, Response } from "express";
|
|
||||||
|
|
||||||
import type { IAccessTokenVerifier, IAccountRepository } from "../../../application";
|
import type { IAccessTokenVerifier, IAccountRepository } from "../../../application";
|
||||||
|
import { authenticateUser } from "./authenticate-user.middleware";
|
||||||
function parseBearerToken(authorization?: string): string | undefined {
|
|
||||||
if (!authorization) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [scheme, token] = authorization.split(" ");
|
|
||||||
if (scheme?.toLowerCase() !== "bearer") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return token?.trim() || undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function identityAuthenticateRequest(params: {
|
export function identityAuthenticateRequest(params: {
|
||||||
accessTokenVerifier: IAccessTokenVerifier;
|
accessTokenVerifier: IAccessTokenVerifier;
|
||||||
accountRepository: IAccountRepository;
|
accountRepository: IAccountRepository;
|
||||||
}) {
|
}) {
|
||||||
return async (req: Request, res: Response, next: NextFunction) => {
|
return authenticateUser(params);
|
||||||
try {
|
|
||||||
const authorization = req.get("authorization");
|
|
||||||
const token = parseBearerToken(authorization);
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
const verifiedTokenResult = await params.accessTokenVerifier.verify(token);
|
|
||||||
if (verifiedTokenResult.isFailure) {
|
|
||||||
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountIdResult = UniqueID.create(verifiedTokenResult.data.accountId);
|
|
||||||
if (accountIdResult.isFailure) {
|
|
||||||
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountResult = await params.accountRepository.findById(accountIdResult.data);
|
|
||||||
if (accountResult.isFailure || accountResult.data.isNone()) {
|
|
||||||
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
const account = accountResult.data.unwrap();
|
|
||||||
const canAuthenticateResult = account.canAuthenticate();
|
|
||||||
if (canAuthenticateResult.isFailure) {
|
|
||||||
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
const emailResult = EmailAddress.create(verifiedTokenResult.data.email);
|
|
||||||
if (emailResult.isFailure) {
|
|
||||||
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
(req as RequestWithAuth).user = {
|
|
||||||
userId: account.id,
|
|
||||||
email: emailResult.data,
|
|
||||||
};
|
|
||||||
|
|
||||||
return next();
|
|
||||||
} catch {
|
|
||||||
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,4 @@
|
|||||||
|
export * from "./authenticate-user.middleware";
|
||||||
|
export * from "./require-authenticated.middleware";
|
||||||
|
export * from "./require-company-context.middleware";
|
||||||
export * from "./identity-authenticate-request";
|
export * from "./identity-authenticate-request";
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { ExpressController, UnauthorizedApiError, type RequestWithAuth } from "@erp/core/api";
|
||||||
|
import type { NextFunction, Response } from "express";
|
||||||
|
|
||||||
|
export function requireAuthenticated() {
|
||||||
|
return (req: RequestWithAuth, res: Response, next: NextFunction) => {
|
||||||
|
if (!req.user) {
|
||||||
|
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { ExpressController, ForbiddenApiError, type RequestWithAuth } from "@erp/core/api";
|
||||||
|
import type { NextFunction, Response } from "express";
|
||||||
|
|
||||||
|
export function requireCompanyContext() {
|
||||||
|
return (req: RequestWithAuth, res: Response, next: NextFunction) => {
|
||||||
|
/**
|
||||||
|
* TODO: validar membership real cuando exista persistencia de CompanyMembership.
|
||||||
|
*/
|
||||||
|
if (!req.user?.companyId) {
|
||||||
|
return ExpressController.errorResponse(
|
||||||
|
new ForbiddenApiError("Company context required"),
|
||||||
|
req,
|
||||||
|
res
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/supplier-invoices",
|
"name": "@erp/supplier-invoices",
|
||||||
"description": "Supplier invoices",
|
"description": "Supplier invoices",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -326,6 +326,7 @@ export class SupplierInvoiceRepository
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
sortableFields: ["invoice_date", "due_date", "id", "created_at"],
|
sortableFields: ["invoice_date", "due_date", "id", "created_at"],
|
||||||
|
fullTextTableAlias: "SupplierInvoiceModel",
|
||||||
enableFullText: true,
|
enableFullText: true,
|
||||||
database: this.database,
|
database: this.database,
|
||||||
strictMode: true,
|
strictMode: true,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/suppliers",
|
"name": "@erp/suppliers",
|
||||||
"description": "Suppliers",
|
"description": "Suppliers",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "uecko-erp-2025",
|
"name": "uecko-erp-2025",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"modules/*",
|
"modules/*",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/i18next",
|
"name": "@repo/i18next",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-criteria",
|
"name": "@repo/rdx-criteria",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-ddd",
|
"name": "@repo/rdx-ddd",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-logger",
|
"name": "@repo/rdx-logger",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-ui",
|
"name": "@repo/rdx-ui",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -77,7 +77,7 @@ export const LineDescriptionField = <TFormValues extends FieldValues>({
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
resizeTextarea();
|
resizeTextarea();
|
||||||
}, [resizeTextarea]);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field className={cn("gap-1", className)} data-invalid={!!fieldError} orientation={orientation}>
|
<Field className={cn("gap-1", className)} data-invalid={!!fieldError} orientation={orientation}>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-utils",
|
"name": "@repo/rdx-utils",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/shadcn-ui",
|
"name": "@repo/shadcn-ui",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/typescript-config",
|
"name": "@repo/typescript-config",
|
||||||
"version": "0.7.8",
|
"version": "0.8.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user