Генерация артикула резолвила lot_category из одного прайслиста конфигурации (GetLocalLotCategoriesByServerPricelistID), поэтому world-only LOT (напр. GPU_NV_RTX_PRO_6000D_SERVER_84GB_PCIE, есть только в world) молча выпадал из артикула. Теперь категории берутся через GetLocalComponentCategoriesByLotNames (тот же world ∪ estimate, что и весь конфигуратор). BuildOptions.ServerPricelist убран; preview-article принимает pricelist_id, но игнорирует. Нераспознанные токены больше не пишутся как UNK: в артикул идёт lot_category как плейсхолдер (4xGPU, 2xCPU), сегмент помечается Recognized=false, добавляется warning с именем LOT. Конфигуратор подсвечивает такие сегменты (amber) и выводит список предупреждений; сохранение/обновление/откат логируют WARN. Без каталога вендоров в репо детектируется только структурный сбой формы имени. parseGPUModel: принимает суффикс-букву в номере модели (6000D → RTX6000D), раньше терял её и схлопывал до RTX_84GB. ADL bible-local/decisions/2026-09-01-article-category-from-component-universe.md, 2026-09-01-article-degraded-token-visibility.md; раздел «Article generation» в 02-architecture.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RAhfF4P1ySRZ67yyUUeVw2
47 lines
2.0 KiB
Go
47 lines
2.0 KiB
Go
package services
|
||
|
||
import (
|
||
"errors"
|
||
|
||
"git.mchus.pro/mchus/quoteforge/internal/models"
|
||
)
|
||
|
||
var (
|
||
ErrConfigNotFound = errors.New("configuration not found")
|
||
ErrConfigForbidden = errors.New("access to configuration forbidden")
|
||
)
|
||
|
||
// ConfigurationGetter is an interface for services that can retrieve configurations.
|
||
// Used by handlers to work with LocalConfigurationService.
|
||
type ConfigurationGetter interface {
|
||
GetByUUID(uuid string, ownerUsername string) (*models.Configuration, error)
|
||
GetByUUIDNoAuth(uuid string) (*models.Configuration, error)
|
||
}
|
||
|
||
type CreateConfigRequest struct {
|
||
Name string `json:"name"`
|
||
Items models.ConfigItems `json:"items"`
|
||
ProjectUUID *string `json:"project_uuid,omitempty"`
|
||
CustomPrice *float64 `json:"custom_price"`
|
||
Notes string `json:"notes"`
|
||
IsTemplate bool `json:"is_template"`
|
||
ServerCount int `json:"server_count"`
|
||
ServerModel string `json:"server_model,omitempty"`
|
||
SupportCode string `json:"support_code,omitempty"`
|
||
Article string `json:"article,omitempty"`
|
||
PricelistID *uint `json:"pricelist_id,omitempty"`
|
||
WarehousePricelistID *uint `json:"warehouse_pricelist_id,omitempty"`
|
||
CompetitorPricelistID *uint `json:"competitor_pricelist_id,omitempty"`
|
||
ConfigType string `json:"config_type,omitempty"` // "server" | "storage"
|
||
DisablePriceRefresh bool `json:"disable_price_refresh"`
|
||
}
|
||
|
||
type ArticlePreviewRequest struct {
|
||
Items models.ConfigItems `json:"items"`
|
||
ServerModel string `json:"server_model"`
|
||
SupportCode string `json:"support_code,omitempty"`
|
||
// PricelistID is accepted for backward compatibility but ignored: article
|
||
// categories come from the component universe (world ∪ estimate), not one pricelist.
|
||
PricelistID *uint `json:"pricelist_id,omitempty"`
|
||
}
|