Arreglo de equivalencias
This commit is contained in:
@@ -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')
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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]")
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user