Arreglo de equivalencias

This commit is contained in:
2026-05-13 08:39:37 -05:00
parent cf27f6103d
commit eced3accdc
7 changed files with 119 additions and 8 deletions

View File

@@ -0,0 +1,75 @@
"""isolate equivalency items
Revision ID: f1a2b3c4d5e6
Revises: e4f5a6b7c8d9
Create Date: 2026-05-12 17:18:00.000000
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
revision: str = "f1a2b3c4d5e6"
down_revision: Union[str, None] = "e4f5a6b7c8d9"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# 1. Add equivalency_id column
op.add_column(
'equivalency_items',
sa.Column(
'equivalency_id',
sa.Integer(),
sa.ForeignKey('a76.equivalencies.id', ondelete='CASCADE'),
nullable=True
),
schema='a76'
)
# 2. Drop old unique constraint
# Note: In some DBs we might need to specify the type, but for Postgres 'unique' or 'foreignkey' works.
op.drop_constraint('uq_equivalency_item_fields', 'equivalency_items', schema='a76', type_='unique')
# 3. Create new unique constraint including equivalency_id
op.create_unique_constraint(
'uq_equivalency_item_fields',
'equivalency_items',
['equivalency_id', 'original_field', 'external_field', 'tenant_id', 'company_id'],
schema='a76'
)
# 4. Increase identifier length in equivalencies table
op.alter_column(
'equivalencies',
'identifier',
type_=sa.String(50),
existing_type=sa.String(10),
schema='a76'
)
def downgrade() -> None:
# 1. Drop new unique constraint
op.drop_constraint('uq_equivalency_item_fields', 'equivalency_items', schema='a76', type_='unique')
# 2. Re-create old unique constraint
op.create_unique_constraint(
'uq_equivalency_item_fields',
'equivalency_items',
['original_field', 'external_field', 'tenant_id', 'company_id'],
schema='a76'
)
# 4. Revert identifier length in equivalencies table
op.alter_column(
'equivalencies',
'identifier',
type_=sa.String(10),
existing_type=sa.String(50),
schema='a76'
)
# 5. Drop equivalency_id column
op.drop_column('equivalency_items', 'equivalency_id', schema='a76')

View File

@@ -10,6 +10,7 @@ class EquivalencyItemBase(BaseModel):
original_field: str = Field(..., max_length=100, description="Campo original (from)")
external_field: str = Field(..., max_length=100, description="Campo externo (to)")
conversion_factor: Optional[Decimal] = Field(None, description="Factor de conversión")
equivalency_id: Optional[int] = Field(None, description="ID de la equivalencia padre")
class EquivalencyItemCreate(EquivalencyItemBase):
@@ -20,6 +21,7 @@ class EquivalencyItemUpdate(BaseModel):
original_field: Optional[str] = Field(None, max_length=100)
external_field: Optional[str] = Field(None, max_length=100)
conversion_factor: Optional[Decimal] = None
equivalency_id: Optional[int] = None
class EquivalencyItemResponse(EquivalencyItemBase):
@@ -34,7 +36,7 @@ class EquivalencyItemResponse(EquivalencyItemBase):
class EquivalencyBase(BaseModel):
identifier: str = Field(..., max_length=10, description="Identificador único")
identifier: str = Field(..., max_length=50, description="Identificador único")
description: Optional[str] = Field(None, max_length=200, description="Descripción (opcional)")
item_id: Optional[int] = Field(None, description="ID del EquivalencyItem seleccionado")
@@ -44,7 +46,7 @@ class EquivalencyCreate(EquivalencyBase):
class EquivalencyUpdate(BaseModel):
identifier: Optional[str] = Field(None, max_length=10)
identifier: Optional[str] = Field(None, max_length=50)
description: Optional[str] = Field(None, max_length=200)
item_id: Optional[int] = Field(None, description="Re-asociar a otro EquivalencyItem")

View File

@@ -13,7 +13,7 @@ class EquivalencyItem(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "equivalency_items"
__table_args__ = (
UniqueConstraint(
"original_field", "external_field", "tenant_id", "company_id",
"equivalency_id", "original_field", "external_field", "tenant_id", "company_id",
name="uq_equivalency_item_fields"
),
{"schema": "a76"}
@@ -27,6 +27,14 @@ class EquivalencyItem(Base, TenantScopedMixin, TimestampMixin):
conversion_factor: Mapped[Optional[Decimal]] = mapped_column(
Numeric(13, 6), nullable=True)
equivalency_id: Mapped[Optional[int]] = mapped_column(
Integer,
ForeignKey("a76.equivalencies.id", ondelete="CASCADE"),
nullable=True)
equivalency: Mapped[Optional["Equivalency"]] = relationship(
back_populates="items", foreign_keys=[equivalency_id])
equivalencies: Mapped[List["Equivalency"]] = relationship(
back_populates="item", foreign_keys="[Equivalency.item_id]")
@@ -44,7 +52,7 @@ class Equivalency(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(
Integer, primary_key=True, autoincrement=True)
identifier: Mapped[str] = mapped_column(String(10), nullable=False)
identifier: Mapped[str] = mapped_column(String(50), nullable=False)
description: Mapped[Optional[str]] = mapped_column(
String(200), nullable=True)
@@ -55,3 +63,6 @@ class Equivalency(Base, TenantScopedMixin, TimestampMixin):
item: Mapped[Optional["EquivalencyItem"]] = relationship(
back_populates="equivalencies", foreign_keys=[item_id])
items: Mapped[List["EquivalencyItem"]] = relationship(
back_populates="equivalency", foreign_keys="[EquivalencyItem.equivalency_id]")

View File

@@ -33,6 +33,10 @@ class EquivalencyItemService:
query = query.filter(
EquivalencyItem.external_field.ilike(f"%{filters['external_field']}%")
)
if "equivalency_id" in filters:
query = query.filter(
EquivalencyItem.equivalency_id == filters["equivalency_id"]
)
total = query.count()
items = query.offset(skip).limit(limit).all()
@@ -63,6 +67,7 @@ class EquivalencyItemService:
original_field=data.original_field,
external_field=data.external_field,
conversion_factor=data.conversion_factor,
equivalency_id=data.equivalency_id,
tenant_id=tenant_id,
company_id=company_id,
)

View File

@@ -10,6 +10,7 @@ export interface EquivalencyItem {
original_field: string;
external_field: string;
conversion_factor: number | null;
equivalency_id: number | null;
tenant_id: number;
company_id: number;
}
@@ -18,12 +19,14 @@ export interface EquivalencyItemCreate {
original_field: string;
external_field: string;
conversion_factor?: number | null;
equivalency_id?: number | null;
}
export interface EquivalencyItemUpdate {
original_field?: string;
external_field?: string;
conversion_factor?: number | null;
equivalency_id?: number | null;
}
export interface EquivalencyItemListResponse {

View File

@@ -16,11 +16,13 @@
let {
open = $bindable(false),
item = null,
equivalency_id = null,
defaultOriginalField = null,
onSuccess
}: {
open: boolean;
item?: EquivalencyItem | null;
equivalency_id?: number | null;
defaultOriginalField?: string | null;
onSuccess?: () => void;
} = $props();
@@ -82,7 +84,8 @@
const dataToSend = {
original_field: formData.original_field.trim(),
external_field: formData.external_field.trim()
external_field: formData.external_field.trim(),
equivalency_id: equivalency_id
};
if (isEdit && item) {

View File

@@ -7,6 +7,7 @@
import * as Table from '$lib/components/ui/table';
import { companyStore } from '$lib/stores/company.svelte';
import { toast } from 'svelte-sonner';
import { friendlyApiErrorParts } from '$lib/api';
import { Check } from 'lucide-svelte';
import type {
Equivalency,
@@ -68,7 +69,17 @@
loading = true;
error = null;
try {
const resp = await getEquivalencyItems(1, 2000, companyId);
const eq = getEffectiveEquivalency();
if (!eq && mode === 'create') {
items = [];
loading = false;
return;
}
const filters: Record<string, any> = {};
if (eq) filters.equivalency_id = eq.id;
const resp = await getEquivalencyItems(1, 2000, companyId, filters);
if (resp.error) throw new Error(resp.error);
items = resp.data.items || [];
} catch (e) {
@@ -144,7 +155,8 @@
companyId
);
if (created.error || !created.data) {
toast.error(created.error || 'Error al guardar');
const { title, description } = friendlyApiErrorParts(created);
toast.error(title, { description });
return;
}
savedEquivalency = created.data;
@@ -248,7 +260,6 @@
function handleInputsSuccess() {
inputsOpen = false;
reloadItems();
onSuccess?.();
}
</script>
@@ -378,6 +389,7 @@
<EquivalencyItemCreateEditDialog
bind:open={inputsOpen}
item={inputsItem}
equivalency_id={getEffectiveEquivalency()?.id ?? null}
defaultOriginalField={inputsItem ? null : null}
onSuccess={handleInputsSuccess}
/>