feat: Add new item management components for invoice editing
- Implemented packages section for item details in `packages-section.svelte` - Created summary section to display general data and weights in `summary-section.svelte` - Developed tab continuation for additional item details in `tab-continuation.svelte` - Added identifiers tab for managing item identifiers in `tab-identifiers.svelte` - Introduced labeling tab for item labeling information in `tab-labeling.svelte` - Created serial numbers tab for entering multiple serial numbers in `tab-series.svelte` - Built item sheet for inventory items in `item-sheet-inv.svelte` - Developed items tab form for managing invoice items in `items-tab-form.svelte`
This commit is contained in:
@@ -38,7 +38,7 @@ class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin):
|
||||
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
|
||||
|
||||
# Identifiers
|
||||
system: Mapped[Optional[str]] = mapped_column(String(10)) # SISTEMA / Sistema de origen <-- no tiene campo en la antigua base de datos, sera para fixed-asset(scaf), inventory(scaii)
|
||||
system: Mapped[Optional[str]] = mapped_column(String(12)) # SISTEMA / Sistema de origen <-- no tiene campo en la antigua base de datos, sera para fixed_asset(scaf), inventory(scaii)
|
||||
operation_type: Mapped[OperationType] = mapped_column(String(10)) # TIPOMOVIMIENTO / Clasifica imp/exp/sm/ctm
|
||||
invoice_type: Mapped[Optional[str]] = mapped_column(ForeignKey("public.invoice_types.key")) # TIPOFACTURA / TIPODOC
|
||||
invoice_number: Mapped[Optional[str]] = mapped_column(String(20)) # FACTURAIMPO/FACTURAEXPO/FACTURAREMISION/FACTURAENVIO/FACTURASALIDA
|
||||
|
||||
@@ -9,7 +9,7 @@ from .models import OperationType
|
||||
class InvoiceHeaderBase(BaseModel):
|
||||
"""Base fields for Invoice Header"""
|
||||
system: Optional[str] = Field(
|
||||
None, max_length=10, description="System of origin")
|
||||
None, max_length=12, description="System of origin")
|
||||
operation_type: Optional[OperationType] = Field(
|
||||
None, max_length=10, description="Operation type: imp/exp/sm/ctm")
|
||||
invoice_type: Optional[str] = Field(
|
||||
|
||||
@@ -28,14 +28,9 @@ class Item(Base, TenantScopedMixin, TimestampMixin):
|
||||
}
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id")) # CONSECUTIVO
|
||||
# Type: IMPORT_TEMP, IMPORT_DEF, EXPORT, REPAIR, etc.
|
||||
item_type: Mapped[str] = mapped_column(String(20))
|
||||
system_origin: Mapped[str] = mapped_column(String(10)) # SCAF or SCAII
|
||||
invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id")) # CONSECUTIVO
|
||||
|
||||
# Item references
|
||||
invoice_number: Mapped[Optional[str]] = mapped_column(
|
||||
String(15)) # FACTURAIMPO/FACTURAEXPO
|
||||
reference_number: Mapped[Optional[str]] = mapped_column(
|
||||
String(20)) # NUMREFERENCIA
|
||||
order: Mapped[Optional[str]] = mapped_column(
|
||||
@@ -44,8 +39,6 @@ class Item(Base, TenantScopedMixin, TimestampMixin):
|
||||
String(50)) # NUMEROGUIA/NUMERODEGUIA
|
||||
|
||||
# Dates
|
||||
invoice_date: Mapped[Optional[int]] = mapped_column(
|
||||
Integer) # FECHAFACTURA
|
||||
depreciation_date: Mapped[Optional[int]] = mapped_column(
|
||||
Integer) # FECHADEPRECIACION
|
||||
|
||||
|
||||
@@ -24,14 +24,6 @@ from .line_items.schemas import (
|
||||
class ItemBase(BaseModel):
|
||||
"""Base schema for items"""
|
||||
invoice_id: int = Field(..., description="Invoice ID")
|
||||
item_type: str = Field(..., max_length=20,
|
||||
description="Item type: IMPORT_TEMP, IMPORT_DEF, EXPORT, REPAIR")
|
||||
system_origin: str = Field(..., max_length=10,
|
||||
description="System origin: SCAF or SCAII")
|
||||
|
||||
# Item references
|
||||
invoice_number: Optional[str] = Field(
|
||||
None, max_length=15, description="Invoice number")
|
||||
reference_number: Optional[str] = Field(
|
||||
None, max_length=20, description="Reference number")
|
||||
order: Optional[str] = Field(None, max_length=50, description="Order")
|
||||
@@ -39,7 +31,6 @@ class ItemBase(BaseModel):
|
||||
None, max_length=50, description="Guide number")
|
||||
|
||||
# Dates
|
||||
invoice_date: Optional[int] = Field(None, description="Invoice date")
|
||||
depreciation_date: Optional[int] = Field(
|
||||
None, description="Depreciation date")
|
||||
|
||||
@@ -60,10 +51,6 @@ class ItemCreate(ItemBase):
|
||||
class ItemUpdate(ItemBase):
|
||||
"""Schema for updating item"""
|
||||
invoice_id: Optional[int] = Field(None, description="Invoice ID")
|
||||
item_type: Optional[str] = Field(
|
||||
None, max_length=20, description="Item type")
|
||||
system_origin: Optional[str] = Field(
|
||||
None, max_length=10, description="System origin")
|
||||
lines: Optional[list[LineItemUpdate]] = Field(
|
||||
None, description="List of line items to update")
|
||||
|
||||
|
||||
@@ -232,10 +232,10 @@ export interface InvoiceListResponse {
|
||||
}
|
||||
|
||||
export interface CreateInvoiceData {
|
||||
system?: string | null;
|
||||
operation_type?: OperationType | null;
|
||||
invoice_type?: string | null;
|
||||
invoice_number?: string | null;
|
||||
system: string;
|
||||
operation_type: OperationType;
|
||||
invoice_type: string;
|
||||
invoice_number: string;
|
||||
project_number?: string | null;
|
||||
purchase_order?: string | null;
|
||||
related_doc_id?: number | null;
|
||||
|
||||
@@ -8,14 +8,10 @@ import { api } from '$lib/api';
|
||||
|
||||
export interface Item {
|
||||
id?: number;
|
||||
invoice_id: number;
|
||||
item_type: string;
|
||||
system_origin: string;
|
||||
invoice_number?: string;
|
||||
invoice_id: number;
|
||||
reference_number?: string;
|
||||
order?: string;
|
||||
guide_number?: string;
|
||||
invoice_date?: number;
|
||||
guide_number?: string;
|
||||
depreciation_date?: number;
|
||||
rectification?: number;
|
||||
warehouse?: string;
|
||||
@@ -33,13 +29,9 @@ export interface ItemListResponse {
|
||||
|
||||
export interface CreateItemData {
|
||||
invoice_id: number;
|
||||
item_type: string;
|
||||
system_origin: string;
|
||||
invoice_number?: string;
|
||||
reference_number?: string;
|
||||
order?: string;
|
||||
guide_number?: string;
|
||||
invoice_date?: number;
|
||||
guide_number?: string;
|
||||
depreciation_date?: number;
|
||||
rectification?: number;
|
||||
warehouse?: string;
|
||||
@@ -47,13 +39,9 @@ export interface CreateItemData {
|
||||
}
|
||||
|
||||
export interface UpdateItemData {
|
||||
item_type?: string;
|
||||
system_origin?: string;
|
||||
invoice_number?: string;
|
||||
reference_number?: string;
|
||||
order?: string;
|
||||
guide_number?: string;
|
||||
invoice_date?: number;
|
||||
guide_number?: string;
|
||||
depreciation_date?: number;
|
||||
rectification?: boolean;
|
||||
warehouse?: string;
|
||||
|
||||
@@ -203,8 +203,7 @@
|
||||
</div>
|
||||
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Clientes - Proveedores - Agente Aduanal</h4>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="provider_id" class="text-xs">Proveedor:</Label>
|
||||
<div class="grid grid-cols-4 gap-3 space-y-1.5">
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.provider_header || ''}
|
||||
@@ -248,11 +247,10 @@
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="sold_to_id" class="text-xs">Consignado a:</Label>
|
||||
<div class="grid grid-cols-4 gap-3 space-y-1.5">
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.sold_to_header || ''}
|
||||
@@ -299,8 +297,7 @@
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="shipped_to_id" class="text-xs">Enviado a:</Label>
|
||||
<div class="grid grid-cols-4 gap-3 space-y-1.5">
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.shipped_to_header || ''}
|
||||
@@ -347,59 +344,58 @@
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="space-y-1.5">
|
||||
<Label for="customs_broker_id" class="text-xs">Agente Aduanal Mex:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.customs_broker_id || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.customs_broker_id = v || null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="customs_broker_id" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.customs_broker_id
|
||||
? customsBrokers.find(cb => cb.broker_key === formData.customs_broker_id)?.name || '...'
|
||||
: '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each customsBrokers as broker}
|
||||
<Select.Item value={broker.broker_key}>
|
||||
{broker.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="customs_broker_id" class="text-xs">Agente Aduanal Mex:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.customs_broker_id || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.customs_broker_id = v || null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="customs_broker_id" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.customs_broker_id
|
||||
? customsBrokers.find(cb => cb.broker_key === formData.customs_broker_id)?.name || '...'
|
||||
: '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each customsBrokers as broker}
|
||||
<Select.Item value={broker.broker_key}>
|
||||
{broker.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="customs_broker_us_id" class="text-xs">Agente Aduanal Ame:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.customs_broker_us_id || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.customs_broker_us_id = v || null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="customs_broker_us_id" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.customs_broker_us_id
|
||||
? customsBrokers.find(cb => cb.broker_key === formData.customs_broker_us_id)?.name || '...'
|
||||
: '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each customsBrokers as broker}
|
||||
<Select.Item value={broker.broker_key}>
|
||||
{broker.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="customs_broker_us_id" class="text-xs">Agente Aduanal Ame:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.customs_broker_us_id || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.customs_broker_us_id = v || null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="customs_broker_us_id" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.customs_broker_us_id
|
||||
? customsBrokers.find(cb => cb.broker_key === formData.customs_broker_us_id)?.name || '...'
|
||||
: '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each customsBrokers as broker}
|
||||
<Select.Item value={broker.broker_key}>
|
||||
{broker.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Columna Derecha: Tipo de Moneda y Transportista -->
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<script lang="ts">
|
||||
import * as RadioGroup from '$lib/components/ui/radio-group';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
|
||||
let {
|
||||
isSubPartida = $bindable(),
|
||||
continueSubPartidas = $bindable()
|
||||
}: {
|
||||
isSubPartida: string;
|
||||
continueSubPartidas: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="lg:col-span-5 space-y-3">
|
||||
<!-- Is Item/Subitem and Continue Sub-Items -->
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<fieldset class="border rounded-md p-2 space-y-2">
|
||||
<legend class="text-xs font-semibold px-2 bg-gray-200 dark:bg-gray-700">Is</legend>
|
||||
<RadioGroup.Root bind:value={isSubPartida} class="flex gap-3">
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroup.Item value="partida" id="partida" />
|
||||
<Label for="partida" class="text-xs font-normal">Item</Label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroup.Item value="subpartida" id="subpartida" />
|
||||
<Label for="subpartida" class="text-xs font-normal">Subitem</Label>
|
||||
</div>
|
||||
</RadioGroup.Root>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="border rounded-md p-2 space-y-2">
|
||||
<legend class="text-xs font-semibold px-2 bg-gray-200 dark:bg-gray-700">Continue Sub-Items</legend>
|
||||
<RadioGroup.Root bind:value={continueSubPartidas} class="flex gap-3">
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroup.Item value="si" id="continue_si" />
|
||||
<Label for="continue_si" class="text-xs font-normal">Yes</Label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroup.Item value="no" id="continue_no" />
|
||||
<Label for="continue_no" class="text-xs font-normal">No</Label>
|
||||
</div>
|
||||
</RadioGroup.Root>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<!-- Descriptions -->
|
||||
<fieldset class="border rounded-md p-2 space-y-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="num_parte" class="text-xs">Part Number:</Label>
|
||||
<div class="flex gap-1">
|
||||
<Input id="num_parte" class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="desc_espanol" class="text-xs">Description in Spanish:</Label>
|
||||
<textarea
|
||||
id="desc_espanol"
|
||||
class="flex min-h-[60px] w-full rounded-md border border-input bg-background px-2 py-1 text-xs ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="desc_ingles" class="text-xs">Description in English:</Label>
|
||||
<textarea
|
||||
id="desc_ingles"
|
||||
class="flex min-h-[60px] w-full rounded-md border border-input bg-background px-2 py-1 text-xs ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
@@ -0,0 +1,124 @@
|
||||
<script lang="ts">
|
||||
import * as Sheet from '$lib/components/ui/sheet';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Loader2 } from 'lucide-svelte';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
import type { Item } from '$lib/api/dashboard/a76/items';
|
||||
|
||||
// Import child components
|
||||
import MainData from './main-data.svelte';
|
||||
import ItemConfiguration from './item-configuration.svelte';
|
||||
import PackagesSection from './packages-section.svelte';
|
||||
import SummarySection from './summary-section.svelte';
|
||||
import TabContinuation from './tab-continuation.svelte';
|
||||
import TabSeries from './tab-series.svelte';
|
||||
import TabLabeling from './tab-labeling.svelte';
|
||||
import TabIdentifiers from './tab-identifiers.svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(),
|
||||
isEditMode = false,
|
||||
editingItem = $bindable(),
|
||||
invoice,
|
||||
onSave,
|
||||
isSaving = false
|
||||
}: {
|
||||
open: boolean;
|
||||
isEditMode?: boolean;
|
||||
editingItem: Partial<Item>;
|
||||
invoice: Invoice | null;
|
||||
onSave: () => void;
|
||||
isSaving?: boolean;
|
||||
} = $props();
|
||||
|
||||
let isSubPartida = $state('partida');
|
||||
let continueSubPartidas = $state('no');
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:global([data-tabs-trigger]) {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<Sheet.Root bind:open={open}>
|
||||
<Sheet.Content side="right" class="w-full sm:max-w-[95vw] lg:max-w-[80vw] xl:max-w-[70vw] overflow-y-auto overflow-x-hidden">
|
||||
<Sheet.Header class="text-white -mx-4 -mt-3 px-6">
|
||||
<Sheet.Title class="text-base font-semibold text-white">
|
||||
Temporary Import Item
|
||||
</Sheet.Title>
|
||||
<Sheet.Description class="text-xs text-purple-100">
|
||||
Order Number: {invoice?.invoice_number || 'N/A'} | Line: 1
|
||||
</Sheet.Description>
|
||||
</Sheet.Header>
|
||||
|
||||
<div class="w-full max-w-full overflow-x-hidden px-1">
|
||||
<!-- Always visible section: Main data and right column -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-4 mb-6">
|
||||
<!-- Left column (7 columns) -->
|
||||
<div class="lg:col-span-7 space-y-3">
|
||||
<MainData />
|
||||
</div>
|
||||
|
||||
<!-- Right column (5 columns) -->
|
||||
<ItemConfiguration bind:isSubPartida={isSubPartida} bind:continueSubPartidas={continueSubPartidas} />
|
||||
</div>
|
||||
|
||||
<!-- Tabs with additional content -->
|
||||
<Tabs.Root value="generales" class="mt-6">
|
||||
<Tabs.List class="grid w-full grid-cols-5 mb-4 h-9 gap-1">
|
||||
<Tabs.Trigger value="generales" class="text-[10px] sm:text-xs px-1 sm:px-2 py-1">1) General</Tabs.Trigger>
|
||||
<Tabs.Trigger value="continuacion" class="text-[10px] sm:text-xs px-1 sm:px-2 py-1">2) Continuation</Tabs.Trigger>
|
||||
<Tabs.Trigger value="series" class="text-[10px] sm:text-xs px-1 sm:px-2 py-1">3) Series</Tabs.Trigger>
|
||||
<Tabs.Trigger value="etiquetado" class="text-[10px] sm:text-xs px-1 sm:px-2 py-1">4) Labeling</Tabs.Trigger>
|
||||
<Tabs.Trigger value="identificadores" class="text-[10px] sm:text-xs px-1 sm:px-2 py-1">5) Identifiers</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
|
||||
<!-- Tab: General -->
|
||||
<Tabs.Content value="generales" class="space-y-3 mt-0">
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<PackagesSection />
|
||||
<SummarySection />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Continuation -->
|
||||
<Tabs.Content value="continuacion" class="space-y-3 mt-0">
|
||||
<TabContinuation />
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Series -->
|
||||
<Tabs.Content value="series" class="space-y-3 mt-0">
|
||||
<TabSeries />
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Labeling -->
|
||||
<Tabs.Content value="etiquetado" class="space-y-3 mt-0">
|
||||
<TabLabeling />
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Identifiers -->
|
||||
<Tabs.Content value="identificadores" class="space-y-3 mt-0">
|
||||
<TabIdentifiers />
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</div>
|
||||
|
||||
<Sheet.Footer>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<Button variant="outline" onclick={() => open = false} disabled={isSaving}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onclick={onSave} disabled={isSaving}>
|
||||
{#if isSaving}
|
||||
<Loader2 class="w-4 h-4 mr-2 animate-spin" />
|
||||
Saving...
|
||||
{:else}
|
||||
{isEditMode ? 'Save Changes' : 'Add Item'}
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</Sheet.Footer>
|
||||
</Sheet.Content>
|
||||
</Sheet.Root>
|
||||
@@ -0,0 +1,74 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
</script>
|
||||
|
||||
<fieldset class="border rounded-md p-3 space-y-3">
|
||||
<legend class="text-xs font-semibold px-2 bg-gray-200 dark:bg-gray-700">Main Data</legend>
|
||||
|
||||
<!-- Class -->
|
||||
<div class="grid grid-cols-12 gap-2">
|
||||
<div class="col-span-4 space-y-1">
|
||||
<Label for="clase" class="text-xs font-medium">* Class:</Label>
|
||||
<div class="flex gap-1">
|
||||
<Input id="clase" class="h-8 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quantity -->
|
||||
<div class="grid grid-cols-12 gap-2">
|
||||
<div class="col-span-4 space-y-1">
|
||||
<Label for="cantidad" class="text-xs font-medium">* Quantity:</Label>
|
||||
<Input id="cantidad" type="number" min="0" value="0.00000000" class="h-8 text-xs text-right" />
|
||||
</div>
|
||||
<div class="col-span-4 space-y-1">
|
||||
<Label class="text-xs font-medium">U.M.:</Label>
|
||||
<div class="flex gap-1">
|
||||
<div class="h-8 flex items-center flex-1">
|
||||
<span class="text-xs text-muted-foreground">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Unit Cost and Fraction -->
|
||||
<div class="grid grid-cols-12 gap-2">
|
||||
<div class="col-span-4 space-y-1">
|
||||
<Label for="costo_unitario" class="text-xs font-medium">* Unit Cost:</Label>
|
||||
<div class="flex items-center gap-2">
|
||||
<Input id="costo_unitario" type="number" min="0" value="0.00000000" class="h-8 text-xs text-right flex-1" />
|
||||
<span class="text-xs text-blue-600 font-semibold">USD</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-5 space-y-1">
|
||||
<Label for="fraccion" class="text-xs font-medium">Fraction:</Label>
|
||||
<div class="flex gap-1">
|
||||
<Input id="fraccion" value="0000 00 00" class="h-8 text-xs text-center" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Origin Country and Tariff Type -->
|
||||
<div class="grid grid-cols-12 gap-2">
|
||||
<div class="col-span-4 space-y-1">
|
||||
<Label for="pais_origen" class="text-xs font-medium">* Origin Country:</Label>
|
||||
<div class="flex gap-1">
|
||||
<Input id="pais_origen" class="h-8 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-4 space-y-1">
|
||||
<Label for="tipo_tarifa" class="text-xs font-medium">* Tariff Type:</Label>
|
||||
<select id="tipo_tarifa" class="flex h-8 w-full rounded-md border border-input bg-background px-2 py-1 text-xs ring-offset-background">
|
||||
<option>GENERAL</option>
|
||||
<option>PREFERENCIAL</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-span-4 space-y-1">
|
||||
<Label class="text-xs font-medium">Advalorem:</Label>
|
||||
<div class="h-8 flex items-center">
|
||||
<span class="text-xs text-muted-foreground">0.00</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,86 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
</script>
|
||||
|
||||
<fieldset class="border rounded-md p-2 space-y-2">
|
||||
<legend class="text-xs font-semibold px-2 bg-gray-200 dark:bg-gray-700">PACKAGES</legend>
|
||||
|
||||
<div class="grid grid-cols-12 gap-2 items-end">
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label for="cantidad_bultos" class="text-xs">Quantity:</Label>
|
||||
<Input id="cantidad_bultos" type="number" min="0" value="0" class="h-7 text-xs text-right" />
|
||||
</div>
|
||||
<div class="col-span-3 space-y-1">
|
||||
<Label for="clave_bultos" class="text-xs">Package Code:</Label>
|
||||
<Input id="clave_bultos" class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="col-span-1 flex items-end">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-12 gap-2 items-end">
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label for="peso_bultos" class="text-xs">Weight: 0</Label>
|
||||
</div>
|
||||
<div class="col-span-4 space-y-1">
|
||||
<Label for="descripcion_bultos" class="text-xs">Description:</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- WEIGHTS subsection -->
|
||||
<div class="border-t pt-2">
|
||||
<div class="text-xs font-semibold mb-2">WEIGHTS</div>
|
||||
<div class="grid grid-cols-6 gap-2 items-end">
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label for="peso_neto" class="text-xs">Net:</Label>
|
||||
<Input id="peso_neto" type="number" min="0" value="0.00000000" class="h-7 text-xs text-right" />
|
||||
</div>
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label for="peso_bruto" class="text-xs">Gross:</Label>
|
||||
<Input id="peso_bruto" type="number" min="0" value="0.00000000" class="h-7 text-xs text-right" />
|
||||
</div>
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label class="text-xs invisible">Space</Label>
|
||||
<span class="text-xs text-red-600 font-semibold">KILOS</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-12 gap-2 items-end">
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label for="no_permiso" class="text-xs">Permit No.:</Label>
|
||||
<Input id="no_permiso" class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="col-span-3 space-y-1">
|
||||
<Label for="pag_region" class="text-xs">Page/Region:</Label>
|
||||
<Input id="pag_region" class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-12 gap-2 items-end">
|
||||
<div class="col-span-4 space-y-1">
|
||||
<Label for="fraccion_americana" class="text-xs">American Fraction:</Label>
|
||||
<Input id="fraccion_americana" class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="col-span-3 space-y-1">
|
||||
<Label class="text-xs invisible">Space</Label>
|
||||
<span class="text-xs">Advalorem: 0.00</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-9 gap-2 items-end">
|
||||
<div class="col-span-3 space-y-1">
|
||||
<Label for="marca" class="text-xs">Brand:</Label>
|
||||
<Input id="marca" class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="col-span-3 space-y-1">
|
||||
<Label for="modelo" class="text-xs">Model:</Label>
|
||||
<Input id="modelo" class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="col-span-3 space-y-1">
|
||||
<Label for="orden_compra" class="text-xs">Purchase Order:</Label>
|
||||
<Input id="orden_compra" class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,46 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<fieldset class="border rounded-md p-2 space-y-1 bg-red-50 dark:bg-red-950/20">
|
||||
<legend class="text-xs font-semibold px-2 bg-red-700 text-white">GENERAL DATA</legend>
|
||||
|
||||
<div class="text-xs font-semibold">RETURN QUANTITY SUB-ITEMS</div>
|
||||
<div class="grid grid-cols-2 gap-2 text-xs">
|
||||
<div>Temporary: <span class="text-blue-600">0.00000000</span></div>
|
||||
<div>Replacement or Change: <span class="text-blue-600">0.00000000</span></div>
|
||||
<div>Definitive: <span class="text-blue-600">0.00000000</span></div>
|
||||
<div>Returned Values: <span class="text-blue-600">0.00000000</span></div>
|
||||
<div class="col-span-2">Returned Values: <span class="text-blue-600">0.00000000</span></div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2 text-xs pt-2 border-t">
|
||||
<div class="font-semibold">WEIGHTS (KILOS)</div>
|
||||
<div class="font-semibold">WEIGHTS (Pounds)</div>
|
||||
<div>Net: <span class="text-blue-600">0.00000000</span></div>
|
||||
<div><span class="text-blue-600">0.00000000</span></div>
|
||||
<div>Whole: <span class="text-blue-600">0.00000000</span></div>
|
||||
<div><span class="text-blue-600">0.00000000</span></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- COSTS AND VALUES -->
|
||||
<fieldset class="border rounded-md p-2 space-y-1 bg-amber-50 dark:bg-amber-950/20">
|
||||
<legend class="text-xs font-semibold px-2 bg-amber-700 text-white">COSTS AND VALUES</legend>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2 text-xs">
|
||||
<div class="font-semibold">(Dollars)</div>
|
||||
<div class="font-semibold">(Pesos)</div>
|
||||
<div>Cost: <span class="text-blue-600">0.00000000</span></div>
|
||||
<div><span class="text-blue-600">0.00000000</span></div>
|
||||
<div>Value: <span class="text-blue-600">0.00000000</span></div>
|
||||
<div><span class="text-blue-600">0.00000000</span></div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1 pt-2 border-t">
|
||||
<div class="text-xs">Capture Cost: <span class="text-blue-600">0.00000000</span> <span class="text-blue-600">USD</span></div>
|
||||
<div class="text-xs">Capture Value: <span class="text-blue-600">0.00000000</span> <span class="text-blue-600">USD</span></div>
|
||||
<div class="text-xs">Customs Value: <span class="text-blue-600">0.00000000</span> <span class="text-blue-600">USD</span></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
@@ -0,0 +1,133 @@
|
||||
<script lang="ts">
|
||||
import * as RadioGroup from '$lib/components/ui/radio-group';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<!-- Left Column -->
|
||||
<div class="space-y-3">
|
||||
<!-- TAX PAID -->
|
||||
<div class="grid grid-cols-4 gap-3">
|
||||
<fieldset class="border rounded-md p-2 space-y-2">
|
||||
<legend class="text-xs font-semibold px-2 bg-gray-200 dark:bg-gray-700">TAX PAID</legend>
|
||||
<RadioGroup.Root value="no" class="flex gap-3">
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroup.Item value="si" id="pago_impuesto_si" />
|
||||
<Label for="pago_impuesto_si" class="text-xs font-normal">Yes</Label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroup.Item value="no" id="pago_impuesto_no" />
|
||||
<Label for="pago_impuesto_no" class="text-xs font-normal">No</Label>
|
||||
</div>
|
||||
</RadioGroup.Root>
|
||||
</fieldset>
|
||||
<div class="space-y-1">
|
||||
<div class="space-y-1">
|
||||
<Label for="forma_pago" class="text-xs">Payment Method:</Label>
|
||||
<div class="flex gap-1">
|
||||
<Input id="forma_pago" value="21" class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
<Label for="credito_iva" class="text-xs">VAT AND EXCISE TAX CREDITS.</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IGI Amount -->
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="monto_igi" class="text-xs">IGI Amount: 0 <span class="text-xs">DOLLARS</span></Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Certificate of Origin -->
|
||||
<div class="grid grid-cols-4 gap-3">
|
||||
<fieldset class="border rounded-md p-2 space-y-2">
|
||||
<legend class="text-xs font-semibold px-2 bg-gray-200 dark:bg-gray-700">Has Certificate of Origin?</legend>
|
||||
<RadioGroup.Root value="no" class="flex gap-3">
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroup.Item value="si" id="cert_origen_si" />
|
||||
<Label for="cert_origen_si" class="text-xs font-normal">Yes</Label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroup.Item value="no" id="cert_origen_no" />
|
||||
<Label for="cert_origen_no" class="text-xs font-normal">No</Label>
|
||||
</div>
|
||||
</RadioGroup.Root>
|
||||
</fieldset>
|
||||
<div class="space-y-1 col-span-3">
|
||||
<Label for="num_cert_origen" class="text-xs">Certificate of Origin No.:</Label>
|
||||
<Input id="num_cert_origen" class="h-7 text-xs" />
|
||||
<Label for="num_cert_origen" class="text-xs">End Date:</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Location -->
|
||||
<div class="space-y-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="localizacion_maquinaria" class="text-xs">Machinery and equipment location:</Label>
|
||||
<div class="flex gap-1">
|
||||
<Input id="localizacion_maquinaria" class="h-7 text-xs" />
|
||||
</div>
|
||||
<Label for="localizacion_maquinaria" class="text-xs">Location variable</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Military Equipment -->
|
||||
<div class="flex items-center space-x-2 ">
|
||||
<Checkbox id="equipo_militar" />
|
||||
<Label for="equipo_militar" class="text-xs font-normal">Enable if Item Contains Military Equipment</Label>
|
||||
</div>
|
||||
|
||||
<!-- Lot and Entry Number -->
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="lote" class="text-xs">Lot:</Label>
|
||||
<Input id="lote" class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="num_entrada" class="text-xs">Entry No.:</Label>
|
||||
<Input id="num_entrada" class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div class="space-y-3">
|
||||
<!-- Permit and Eighth Rule Fraction -->
|
||||
<div class="space-y-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="permiso_regla_octava" class="text-xs">Eighth Rule Permit:</Label>
|
||||
<div class="flex gap-1">
|
||||
<Input id="permiso_regla_octava" class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 gap-2 items-end">
|
||||
<div class="space-y-1 col-span-3">
|
||||
<Label for="fraccion_regla_octava" class="text-xs">Eighth Rule Fraction:</Label>
|
||||
<Input id="fraccion_regla_octava" value="0000.00.00" class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="linea_regla" class="text-xs">Line:</Label>
|
||||
<Input id="linea_regla" type="number" min="0" value="0" class="h-7 text-xs text-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Consider in a31 -->
|
||||
<div class="flex items-center space-x-2 ">
|
||||
<Checkbox id="a31" />
|
||||
<Label for="a31" class="text-xs font-normal">Consider in A31</Label>
|
||||
</div>
|
||||
|
||||
<!-- Extra Description -->
|
||||
<div class="space-y-1">
|
||||
<Label for="desc_extra_espanol" class="text-xs">Extra Description in Spanish:</Label>
|
||||
<textarea
|
||||
id="desc_extra_espanol"
|
||||
class="flex min-h-[80px] w-full rounded-md border border-input bg-background px-2 py-1 text-xs ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
</script>
|
||||
|
||||
<fieldset class="border rounded-md p-3 space-y-3">
|
||||
<legend class="text-xs font-semibold px-2 uppercase">Identifiers</legend>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="identificador1" class="text-xs">Identifier 1:</Label>
|
||||
<Input id="identificador1" class="h-8 text-sm" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="identificador2" class="text-xs">Identifier 2:</Label>
|
||||
<Input id="identificador2" class="h-8 text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="identificador3" class="text-xs">Identifier 3:</Label>
|
||||
<Input id="identificador3" class="h-8 text-sm" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="identificador4" class="text-xs">Identifier 4:</Label>
|
||||
<Input id="identificador4" class="h-8 text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="notas_identificadores" class="text-xs">Notes:</Label>
|
||||
<textarea
|
||||
id="notas_identificadores"
|
||||
class="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
placeholder="Notes about identifiers..."
|
||||
></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
</script>
|
||||
|
||||
<fieldset class="border rounded-md p-3 space-y-3">
|
||||
<legend class="text-xs font-semibold px-2 uppercase">Labeling</legend>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="numero_etiqueta" class="text-xs">Label Number:</Label>
|
||||
<Input id="numero_etiqueta" class="h-8 text-sm" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="tipo_etiqueta" class="text-xs">Label Type:</Label>
|
||||
<Input id="tipo_etiqueta" class="h-8 text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="observaciones_etiqueta" class="text-xs">Observations:</Label>
|
||||
<textarea
|
||||
id="observaciones_etiqueta"
|
||||
class="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
placeholder="Labeling observations..."
|
||||
></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
</script>
|
||||
|
||||
<fieldset class="border rounded-md p-3 space-y-3">
|
||||
<legend class="text-xs font-semibold px-2 uppercase">Serial Numbers</legend>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="series" class="text-xs">Serial Numbers:</Label>
|
||||
<textarea
|
||||
id="series"
|
||||
class="flex min-h-[100px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
placeholder="Enter serial numbers, one per line..."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-muted-foreground">
|
||||
You can enter multiple serial numbers, one per line
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,274 @@
|
||||
<script lang="ts">
|
||||
import * as Sheet from '$lib/components/ui/sheet';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Loader2 } from 'lucide-svelte';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
import type { Item } from '$lib/api/dashboard/a76/items';
|
||||
|
||||
let {
|
||||
open = $bindable(),
|
||||
isEditMode = false,
|
||||
editingItem = $bindable(),
|
||||
invoice,
|
||||
onSave,
|
||||
isSaving = false
|
||||
}: {
|
||||
open: boolean;
|
||||
isEditMode?: boolean;
|
||||
editingItem: Partial<Item>;
|
||||
invoice: Invoice | null;
|
||||
onSave: () => void;
|
||||
isSaving?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Sheet.Root bind:open={open}>
|
||||
<Sheet.Content side="right" class="w-full sm:max-w-2xl overflow-y-auto">
|
||||
<Sheet.Header>
|
||||
<Sheet.Title>{isEditMode ? 'Editar Item' : 'Agregar Nuevo Item'} - SCAII (Inventario)</Sheet.Title>
|
||||
<Sheet.Description>
|
||||
{isEditMode ? 'Modifica los campos del inventario y guarda los cambios.' : 'Completa la información del nuevo item de inventario.'}
|
||||
</Sheet.Description>
|
||||
</Sheet.Header>
|
||||
|
||||
<Tabs.Root value="general" class="mt-6">
|
||||
<Tabs.List class="grid w-full grid-cols-4">
|
||||
<Tabs.Trigger value="general">General</Tabs.Trigger>
|
||||
<Tabs.Trigger value="clasificacion">Clasificación</Tabs.Trigger>
|
||||
<Tabs.Trigger value="cantidades">Cantidades</Tabs.Trigger>
|
||||
<Tabs.Trigger value="otros">Otros</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
|
||||
<!-- Tab: General -->
|
||||
<Tabs.Content value="general" class="space-y-4 mt-4">
|
||||
<!-- Información de la Factura (Solo lectura) -->
|
||||
<div class="rounded-lg border bg-muted/50 p-4 space-y-3">
|
||||
<h4 class="text-sm font-medium">Información de la Factura (SCAII - Inventario)</h4>
|
||||
{#if !invoice?.id}
|
||||
<div class="text-sm text-amber-600 bg-amber-50 dark:bg-amber-950/20 p-3 rounded">
|
||||
⚠️ Esta factura aún no se ha guardado. Los items se asociarán cuando guardes la factura.
|
||||
</div>
|
||||
{:else}
|
||||
<div class="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span class="text-muted-foreground">ID Factura:</span>
|
||||
<span class="ml-2 font-medium">{invoice.id}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted-foreground">Tipo Operación:</span>
|
||||
<span class="ml-2 font-medium uppercase">{invoice.operation_type || 'N/A'}</span>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<span class="text-muted-foreground">Número de Factura:</span>
|
||||
<span class="ml-2 font-medium">{invoice.invoice_number || 'Pendiente'}</span>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<span class="text-muted-foreground">Sistema:</span>
|
||||
<span class="ml-2 font-medium bg-blue-100 dark:bg-blue-900/30 px-2 py-1 rounded">SCAII (Inventory)</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="reference_number">Número de Referencia</Label>
|
||||
<Input id="reference_number" bind:value={editingItem.reference_number} />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="order">Orden de Compra/Venta</Label>
|
||||
<Input
|
||||
id="order"
|
||||
bind:value={editingItem.order}
|
||||
placeholder={invoice?.purchase_order || ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="warehouse">Almacén</Label>
|
||||
<Input id="warehouse" bind:value={editingItem.warehouse} />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="location">Ubicación</Label>
|
||||
<Input id="location" bind:value={editingItem.location} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campos específicos de SCAII -->
|
||||
<div class="space-y-2">
|
||||
<Label for="product_description">Descripción del Producto</Label>
|
||||
<Input id="product_description" placeholder="Descripción detallada del producto" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="sku">SKU</Label>
|
||||
<Input id="sku" placeholder="Código SKU del producto" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="batch">Lote</Label>
|
||||
<Input id="batch" placeholder="Número de lote" />
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Clasificación -->
|
||||
<Tabs.Content value="clasificacion" class="space-y-4 mt-4">
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="tariff_fraction">Fracción Arancelaria</Label>
|
||||
<Input id="tariff_fraction" placeholder="8 dígitos" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="product_type">Tipo de Producto</Label>
|
||||
<Input id="product_type" placeholder="Materia prima, producto terminado, etc." />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="material_type">Tipo de Material</Label>
|
||||
<Input id="material_type" placeholder="Metal, plástico, etc." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="product_code">Código de Producto</Label>
|
||||
<Input id="product_code" placeholder="Código interno" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="country_origin">País de Origen</Label>
|
||||
<Input id="country_origin" placeholder="Código del país" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="merchandise_category">Categoría de Mercancía</Label>
|
||||
<Input id="merchandise_category" placeholder="Categoría" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Cantidades -->
|
||||
<Tabs.Content value="cantidades" class="space-y-4 mt-4">
|
||||
<div class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="quantity">Cantidad</Label>
|
||||
<Input id="quantity" type="number" placeholder="0" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="unit">Unidad de Medida</Label>
|
||||
<Input id="unit" placeholder="PZA, KG, M, etc." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="net_weight">Peso Neto (KG)</Label>
|
||||
<Input id="net_weight" type="number" step="0.01" placeholder="0.00" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="gross_weight">Peso Bruto (KG)</Label>
|
||||
<Input id="gross_weight" type="number" step="0.01" placeholder="0.00" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="unit_value">Valor Unitario (USD)</Label>
|
||||
<Input id="unit_value" type="number" step="0.01" placeholder="0.00" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="total_value">Valor Total (USD)</Label>
|
||||
<Input id="total_value" type="number" step="0.01" placeholder="0.00" disabled />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campos específicos de SCAII -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="packages">Número de Bultos</Label>
|
||||
<Input id="packages" type="number" placeholder="0" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="package_type">Tipo de Empaque</Label>
|
||||
<Input id="package_type" placeholder="Caja, pallet, etc." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="imported_quantity">Cantidad Importada</Label>
|
||||
<Input id="imported_quantity" type="number" placeholder="0" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="remaining_quantity">Cantidad Remanente</Label>
|
||||
<Input id="remaining_quantity" type="number" placeholder="0" disabled />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Otros -->
|
||||
<Tabs.Content value="otros" class="space-y-4 mt-4">
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="brand">Marca</Label>
|
||||
<Input id="brand" placeholder="Marca del producto" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="expiration_date">Fecha de Caducidad</Label>
|
||||
<Input id="expiration_date" type="date" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="production_date">Fecha de Producción</Label>
|
||||
<Input id="production_date" type="date" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="min_stock">Stock Mínimo</Label>
|
||||
<Input id="min_stock" type="number" placeholder="0" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="max_stock">Stock Máximo</Label>
|
||||
<Input id="max_stock" type="number" placeholder="0" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="observations">Observaciones</Label>
|
||||
<textarea
|
||||
id="observations"
|
||||
class="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
placeholder="Notas adicionales sobre el inventario..."
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
|
||||
<Sheet.Footer class="mt-6 gap-2">
|
||||
<Button variant="outline" onclick={() => open = false} disabled={isSaving}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button onclick={onSave} disabled={isSaving}>
|
||||
{#if isSaving}
|
||||
<Loader2 class="w-4 h-4 mr-2 animate-spin" />
|
||||
Guardando...
|
||||
{:else}
|
||||
{isEditMode ? 'Guardar Cambios' : 'Agregar Item'}
|
||||
{/if}
|
||||
</Button>
|
||||
</Sheet.Footer>
|
||||
</Sheet.Content>
|
||||
</Sheet.Root>
|
||||
@@ -1,16 +1,14 @@
|
||||
<script lang="ts">
|
||||
import * as Table from '$lib/components/ui/table';
|
||||
import * as Dialog from '$lib/components/ui/dialog';
|
||||
import * as Sheet from '$lib/components/ui/sheet';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Plus, Pencil, Trash2, X, Loader2 } from 'lucide-svelte';
|
||||
import { Plus, Pencil, Trash2, Loader2 } from 'lucide-svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
import { itemsApi, type Item } from '$lib/api/dashboard/a76/items';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
import ItemSheetFa from './fa/item-sheet-fa.svelte';
|
||||
import ItemSheetInv from './inv/item-sheet-inv.svelte';
|
||||
|
||||
let {
|
||||
invoice,
|
||||
@@ -42,14 +40,15 @@
|
||||
let selectedItem = $state<Item | null>(null);
|
||||
let editingItem = $state<Partial<Item>>({
|
||||
invoice_id: undefined,
|
||||
item_type: '',
|
||||
invoice_number: '',
|
||||
reference_number: '',
|
||||
order: '',
|
||||
warehouse: '',
|
||||
location: ''
|
||||
});
|
||||
|
||||
// Determinar el tipo de sistema (SCAF o SCAII)
|
||||
const invoiceSystem = $derived(invoice?.system || 'scaii'); // Por defecto SCAII si no se especifica
|
||||
|
||||
// Derived value para company ID
|
||||
const activeCompanyId = $derived(companyStore.activeCompany?.id);
|
||||
|
||||
@@ -116,8 +115,6 @@
|
||||
// Auto-asignar valores desde la factura
|
||||
editingItem = {
|
||||
invoice_id: invoice.id,
|
||||
item_type: invoice.operation_type || '',
|
||||
invoice_number: invoice.invoice_number || '',
|
||||
reference_number: '',
|
||||
order: invoice.purchase_order || '',
|
||||
warehouse: '',
|
||||
@@ -144,9 +141,6 @@
|
||||
try {
|
||||
const response = await itemsApi.create(activeCompanyId, {
|
||||
invoice_id: invoice.id,
|
||||
item_type: editingItem.item_type || '',
|
||||
system_origin: 'SCAF', // Por defecto SCAF, podrías hacerlo configurable
|
||||
invoice_number: editingItem.invoice_number,
|
||||
reference_number: editingItem.reference_number,
|
||||
order: editingItem.order,
|
||||
warehouse: editingItem.warehouse,
|
||||
@@ -176,10 +170,7 @@
|
||||
|
||||
isSaving = true;
|
||||
try {
|
||||
await itemsApi.update(selectedItem.id, activeCompanyId, {
|
||||
item_type: editingItem.item_type,
|
||||
system_origin: editingItem.system_origin,
|
||||
invoice_number: editingItem.invoice_number,
|
||||
await itemsApi.update(selectedItem.id, activeCompanyId, {
|
||||
reference_number: editingItem.reference_number,
|
||||
order: editingItem.order,
|
||||
warehouse: editingItem.warehouse,
|
||||
@@ -256,8 +247,6 @@
|
||||
<Table.Root>
|
||||
<Table.Header class="sticky top-0 bg-background z-10">
|
||||
<Table.Row>
|
||||
<Table.Head class="w-[100px]">Tipo</Table.Head>
|
||||
<Table.Head>Factura</Table.Head>
|
||||
<Table.Head>Referencia</Table.Head>
|
||||
<Table.Head>Orden</Table.Head>
|
||||
<Table.Head>Almacén</Table.Head>
|
||||
@@ -275,8 +264,6 @@
|
||||
{:else}
|
||||
{#each displayedItems as item (item.id)}
|
||||
<Table.Row>
|
||||
<Table.Cell class="font-medium">{item.item_type}</Table.Cell>
|
||||
<Table.Cell>{item.invoice_number || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.reference_number || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.order || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.warehouse || '-'}</Table.Cell>
|
||||
@@ -343,150 +330,26 @@
|
||||
</div>
|
||||
|
||||
<!-- Item Sheet (Panel lateral para agregar/editar) -->
|
||||
<Sheet.Root bind:open={showItemSheet}>
|
||||
<Sheet.Content side="right" class="w-full sm:max-w-2xl overflow-y-auto">
|
||||
<Sheet.Header>
|
||||
<Sheet.Title>{isEditMode ? 'Editar Item' : 'Agregar Nuevo Item'}</Sheet.Title>
|
||||
<Sheet.Description>
|
||||
{isEditMode ? 'Modifica los campos del item y guarda los cambios.' : 'Completa la información del nuevo item.'}
|
||||
</Sheet.Description>
|
||||
</Sheet.Header>
|
||||
|
||||
<Tabs.Root value="general" class="mt-6">
|
||||
<Tabs.List class="grid w-full grid-cols-4">
|
||||
<Tabs.Trigger value="general">General</Tabs.Trigger>
|
||||
<Tabs.Trigger value="clasificacion">Clasificación</Tabs.Trigger>
|
||||
<Tabs.Trigger value="cantidades">Cantidades</Tabs.Trigger>
|
||||
<Tabs.Trigger value="otros">Otros</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
|
||||
<!-- Tab: General -->
|
||||
<Tabs.Content value="general" class="space-y-4 mt-4">
|
||||
<!-- Información de la Factura (Solo lectura) -->
|
||||
<div class="rounded-lg border bg-muted/50 p-4 space-y-3">
|
||||
<h4 class="text-sm font-medium">Información de la Factura</h4>
|
||||
{#if !invoice?.id}
|
||||
<div class="text-sm text-amber-600 bg-amber-50 dark:bg-amber-950/20 p-3 rounded">
|
||||
⚠️ Esta factura aún no se ha guardado. Los items se asociarán cuando guardes la factura.
|
||||
</div>
|
||||
{:else}
|
||||
<div class="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span class="text-muted-foreground">ID Factura:</span>
|
||||
<span class="ml-2 font-medium">{invoice.id}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted-foreground">Tipo Operación:</span>
|
||||
<span class="ml-2 font-medium uppercase">{invoice.operation_type || 'N/A'}</span>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<span class="text-muted-foreground">Número de Factura:</span>
|
||||
<span class="ml-2 font-medium">{invoice.invoice_number || 'Pendiente'}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="reference_number">Número de Referencia</Label>
|
||||
<Input id="reference_number" bind:value={editingItem.reference_number} />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="order">Orden de Compra/Venta</Label>
|
||||
<Input
|
||||
id="order"
|
||||
bind:value={editingItem.order}
|
||||
placeholder={invoice?.purchase_order || ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="warehouse">Almacén</Label>
|
||||
<Input id="warehouse" bind:value={editingItem.warehouse} />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="location">Ubicación</Label>
|
||||
<Input id="location" bind:value={editingItem.location} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="invoice_date">Fecha de Factura</Label>
|
||||
<Input
|
||||
id="invoice_date"
|
||||
type="date"
|
||||
bind:value={editingItem.invoice_date}
|
||||
/>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Clasificación -->
|
||||
<Tabs.Content value="clasificacion" class="space-y-4 mt-4">
|
||||
<div class="rounded-lg border p-4">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Aquí puedes agregar campos de clasificación como:
|
||||
</p>
|
||||
<ul class="mt-2 text-sm text-muted-foreground list-disc list-inside">
|
||||
<li>Fracción arancelaria</li>
|
||||
<li>Código de producto</li>
|
||||
<li>Clasificación SCAC</li>
|
||||
<li>Material type</li>
|
||||
<li>Categoría de mercancía</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Cantidades -->
|
||||
<Tabs.Content value="cantidades" class="space-y-4 mt-4">
|
||||
<div class="rounded-lg border p-4">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Aquí puedes agregar campos de cantidades como:
|
||||
</p>
|
||||
<ul class="mt-2 text-sm text-muted-foreground list-disc list-inside">
|
||||
<li>Cantidad</li>
|
||||
<li>Unidad de medida</li>
|
||||
<li>Peso neto</li>
|
||||
<li>Peso bruto</li>
|
||||
<li>Valor unitario</li>
|
||||
<li>Valor total</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- Tab: Otros -->
|
||||
<Tabs.Content value="otros" class="space-y-4 mt-4">
|
||||
<div class="rounded-lg border p-4">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Aquí puedes agregar otros campos como:
|
||||
</p>
|
||||
<ul class="mt-2 text-sm text-muted-foreground list-disc list-inside">
|
||||
<li>País de origen</li>
|
||||
<li>Observaciones</li>
|
||||
<li>Documentos adjuntos</li>
|
||||
<li>Información adicional</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
|
||||
<Sheet.Footer class="mt-6 gap-2">
|
||||
<Button variant="outline" onclick={() => showItemSheet = false} disabled={isSaving}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button onclick={saveItem} disabled={isSaving}>
|
||||
{#if isSaving}
|
||||
<Loader2 class="w-4 h-4 mr-2 animate-spin" />
|
||||
Guardando...
|
||||
{:else}
|
||||
{isEditMode ? 'Guardar Cambios' : 'Agregar Item'}
|
||||
{/if}
|
||||
</Button>
|
||||
</Sheet.Footer>
|
||||
</Sheet.Content>
|
||||
</Sheet.Root>
|
||||
<!-- Renderizar el componente apropiado según el sistema -->
|
||||
{#if invoiceSystem === 'fixed_asset'}
|
||||
<ItemSheetFa
|
||||
bind:open={showItemSheet}
|
||||
{isEditMode}
|
||||
bind:editingItem={editingItem}
|
||||
{invoice}
|
||||
onSave={saveItem}
|
||||
{isSaving}
|
||||
/>
|
||||
{:else}
|
||||
<ItemSheetInv
|
||||
bind:open={showItemSheet}
|
||||
{isEditMode}
|
||||
bind:editingItem={editingItem}
|
||||
{invoice}
|
||||
onSave={saveItem}
|
||||
{isSaving}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Delete Confirmation Dialog -->
|
||||
<Dialog.Root bind:open={showDeleteDialog}>
|
||||
@@ -76,6 +76,7 @@ function buildInvoicePayload(formData: FormDataSet): CreateInvoiceData | UpdateI
|
||||
|
||||
const payload: CreateInvoiceData | UpdateInvoiceData = {
|
||||
// Datos generales desde el formulario general
|
||||
system: 'fixed_asset',
|
||||
operation_type: generalFormData?.operation_type !== null && generalFormData?.operation_type !== undefined
|
||||
? (generalFormData.operation_type === 1 ? 'exp' : 'imp') as OperationType
|
||||
: undefined,
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
// Importar los componentes de cada pestaña
|
||||
import GeneralTabForm from '$lib/components/dashboard/invoices/edit/general-tab-form.svelte';
|
||||
import ObservationsTabForm from '$lib/components/dashboard/invoices/edit/observations-tab-form.svelte';
|
||||
import ItemsTabForm from '$lib/components/dashboard/invoices/edit/items-tab-form.svelte';
|
||||
import ItemsTabForm from '$lib/components/dashboard/invoices/edit/items/items-tab-form.svelte';
|
||||
import OthersTabForm from '$lib/components/dashboard/invoices/edit/others-tab-form.svelte';
|
||||
import InvoiceTopFields from '$lib/components/dashboard/invoices/edit/invoice-top-fields.svelte';
|
||||
import ContinuationTabForm from '$lib/components/dashboard/invoices/edit/continuation-tab-form.svelte';
|
||||
|
||||
Reference in New Issue
Block a user