refactor: move sourcing_link and standard_cost from item columns to revision properties

- Add migration 013 to copy sourcing_link/standard_cost values into
  current revision properties JSONB and drop the columns from items table
- Remove SourcingLink/StandardCost from Go Item struct and all DB queries
  (items.go, audit_queries.go, projects.go)
- Remove from API request/response structs and handlers
- Update CSV/ODS/BOM export/import to read these from revision properties
- Update audit handlers to score as regular property fields
- Remove from frontend Item type and hardcoded form fields
- MainTab now reads sourcing_link/standard_cost from item.properties
- CreateItemPane/EditItemPane no longer have dedicated fields for these;
  they will be rendered as schema-driven property fields
This commit is contained in:
2026-02-11 09:50:31 -06:00
parent 2157b40d06
commit b3c748ef10
16 changed files with 483 additions and 300 deletions

View File

@@ -24,11 +24,9 @@ type Item struct {
CADFilePath *string
CreatedBy *string
UpdatedBy *string
SourcingType string // "manufactured" or "purchased"
SourcingLink *string // URL to supplier/datasheet
LongDescription *string // extended description
StandardCost *float64 // baseline unit cost
ThumbnailKey *string // MinIO key for item thumbnail
SourcingType string // "manufactured" or "purchased"
LongDescription *string // extended description
ThumbnailKey *string // MinIO key for item thumbnail
}
// Revision represents a revision record.
@@ -96,11 +94,11 @@ func (r *ItemRepository) Create(ctx context.Context, item *Item, properties map[
}
err := tx.QueryRow(ctx, `
INSERT INTO items (part_number, schema_id, item_type, description, created_by,
sourcing_type, sourcing_link, long_description, standard_cost)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
sourcing_type, long_description)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, created_at, updated_at, current_revision
`, item.PartNumber, item.SchemaID, item.ItemType, item.Description, item.CreatedBy,
sourcingType, item.SourcingLink, item.LongDescription, item.StandardCost,
sourcingType, item.LongDescription,
).Scan(
&item.ID, &item.CreatedAt, &item.UpdatedAt, &item.CurrentRevision,
)
@@ -133,7 +131,7 @@ func (r *ItemRepository) GetByPartNumber(ctx context.Context, partNumber string)
SELECT id, part_number, schema_id, item_type, description,
created_at, updated_at, archived_at, current_revision,
cad_synced_at, cad_file_path,
sourcing_type, sourcing_link, long_description, standard_cost,
sourcing_type, long_description,
thumbnail_key
FROM items
WHERE part_number = $1 AND archived_at IS NULL
@@ -141,7 +139,7 @@ func (r *ItemRepository) GetByPartNumber(ctx context.Context, partNumber string)
&item.ID, &item.PartNumber, &item.SchemaID, &item.ItemType, &item.Description,
&item.CreatedAt, &item.UpdatedAt, &item.ArchivedAt, &item.CurrentRevision,
&item.CADSyncedAt, &item.CADFilePath,
&item.SourcingType, &item.SourcingLink, &item.LongDescription, &item.StandardCost,
&item.SourcingType, &item.LongDescription,
&item.ThumbnailKey,
)
if err == pgx.ErrNoRows {
@@ -160,7 +158,7 @@ func (r *ItemRepository) GetByID(ctx context.Context, id string) (*Item, error)
SELECT id, part_number, schema_id, item_type, description,
created_at, updated_at, archived_at, current_revision,
cad_synced_at, cad_file_path,
sourcing_type, sourcing_link, long_description, standard_cost,
sourcing_type, long_description,
thumbnail_key
FROM items
WHERE id = $1
@@ -168,7 +166,7 @@ func (r *ItemRepository) GetByID(ctx context.Context, id string) (*Item, error)
&item.ID, &item.PartNumber, &item.SchemaID, &item.ItemType, &item.Description,
&item.CreatedAt, &item.UpdatedAt, &item.ArchivedAt, &item.CurrentRevision,
&item.CADSyncedAt, &item.CADFilePath,
&item.SourcingType, &item.SourcingLink, &item.LongDescription, &item.StandardCost,
&item.SourcingType, &item.LongDescription,
&item.ThumbnailKey,
)
if err == pgx.ErrNoRows {
@@ -192,7 +190,7 @@ func (r *ItemRepository) List(ctx context.Context, opts ListOptions) ([]*Item, e
query = `
SELECT DISTINCT i.id, i.part_number, i.schema_id, i.item_type, i.description,
i.created_at, i.updated_at, i.archived_at, i.current_revision,
i.sourcing_type, i.sourcing_link, i.long_description, i.standard_cost,
i.sourcing_type, i.long_description,
i.thumbnail_key
FROM items i
JOIN item_projects ip ON ip.item_id = i.id
@@ -205,7 +203,7 @@ func (r *ItemRepository) List(ctx context.Context, opts ListOptions) ([]*Item, e
query = `
SELECT id, part_number, schema_id, item_type, description,
created_at, updated_at, archived_at, current_revision,
sourcing_type, sourcing_link, long_description, standard_cost,
sourcing_type, long_description,
thumbnail_key
FROM items
WHERE archived_at IS NULL
@@ -257,7 +255,7 @@ func (r *ItemRepository) List(ctx context.Context, opts ListOptions) ([]*Item, e
err := rows.Scan(
&item.ID, &item.PartNumber, &item.SchemaID, &item.ItemType, &item.Description,
&item.CreatedAt, &item.UpdatedAt, &item.ArchivedAt, &item.CurrentRevision,
&item.SourcingType, &item.SourcingLink, &item.LongDescription, &item.StandardCost,
&item.SourcingType, &item.LongDescription,
&item.ThumbnailKey,
)
if err != nil {
@@ -659,9 +657,7 @@ type UpdateItemFields struct {
Description string
UpdatedBy *string
SourcingType *string
SourcingLink *string
LongDescription *string
StandardCost *float64
}
// Update modifies an item's fields. The UUID remains stable.
@@ -670,16 +666,12 @@ func (r *ItemRepository) Update(ctx context.Context, id string, fields UpdateIte
UPDATE items
SET part_number = $2, item_type = $3, description = $4, updated_by = $5,
sourcing_type = COALESCE($6, sourcing_type),
sourcing_link = CASE WHEN $7::boolean THEN $8 ELSE sourcing_link END,
long_description = CASE WHEN $9::boolean THEN $10 ELSE long_description END,
standard_cost = CASE WHEN $11::boolean THEN $12 ELSE standard_cost END,
long_description = CASE WHEN $7::boolean THEN $8 ELSE long_description END,
updated_at = now()
WHERE id = $1 AND archived_at IS NULL
`, id, fields.PartNumber, fields.ItemType, fields.Description, fields.UpdatedBy,
fields.SourcingType,
fields.SourcingLink != nil, fields.SourcingLink,
fields.LongDescription != nil, fields.LongDescription,
fields.StandardCost != nil, fields.StandardCost,
)
if err != nil {
return fmt.Errorf("updating item: %w", err)