feat: расчёт стоимости платного тестирования/аренды GPU-серверов

Новая вкладка «Аренда» в конфигураторе (доступна для проектов с флагом
rental_enabled): расчёт по методичке "Регламент расчёта стоимости
платного тестирования и аренды GPU-серверов" — Разовый + Еженедельный
платёж по каждому компоненту (New/БУ чекбоксом), с аплифтом к Estimate.

Заодно: поддержка (support_code) в Base-вкладке теперь добавляется как
обычный LOT в спеку (SVC_{срок}y{уровень}_{платформа}) через
autocomplete-пикер вместо выпадающего списка — партномер собирается
тем же lot_name-based механизмом, что и для GPU/CPU/памяти, справочная
цена по регламенту техподдержки хранится в qt_settings.support_pricing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-07-22 13:34:07 +03:00
co-authored by Claude Sonnet 5
parent b48436de93
commit 7edb80e498
25 changed files with 1052 additions and 71 deletions
+37
View File
@@ -64,6 +64,41 @@ type VendorSpecItem struct {
LotMappings []VendorSpecLotMapping `json:"lot_mappings,omitempty"`
}
// RentalItemCondition tracks whether a component is being rented/tested as
// new or used hardware, for a single configuration's Аренда tab. This is
// independent of the lot's catalog data: the same lot_name can be "new" in
// one configuration and "used" in another.
type RentalItemCondition struct {
LotName string `json:"lot_name"`
Condition string `json:"condition"` // "new" or "used"
}
type RentalItemConditions []RentalItemCondition
func (r RentalItemConditions) Value() (driver.Value, error) {
if r == nil {
return nil, nil
}
return json.Marshal(r)
}
func (r *RentalItemConditions) Scan(value interface{}) error {
if value == nil {
*r = nil
return nil
}
var bytes []byte
switch v := value.(type) {
case []byte:
bytes = v
case string:
bytes = []byte(v)
default:
return errors.New("type assertion failed for RentalItemConditions")
}
return json.Unmarshal(bytes, r)
}
type VendorSpec []VendorSpecItem
func (v VendorSpec) Value() (driver.Value, error) {
@@ -114,6 +149,8 @@ type Configuration struct {
ConfigType string `gorm:"size:20;default:server" json:"config_type"` // "server" | "storage"
DisablePriceRefresh bool `gorm:"default:false" json:"disable_price_refresh"`
OnlyInStock bool `gorm:"default:false" json:"only_in_stock"`
RentalItems RentalItemConditions `gorm:"type:json" json:"rental_items,omitempty"`
RentalUpliftPercent float64 `gorm:"type:decimal(8,2);default:0" json:"rental_uplift_percent"`
Line int `gorm:"column:line_no;index" json:"line"`
PriceUpdatedAt *time.Time `gorm:"type:timestamp" json:"price_updated_at,omitempty"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
+1
View File
@@ -12,6 +12,7 @@ type Project struct {
TrackerURL string `gorm:"size:500" json:"tracker_url"`
IsActive bool `gorm:"default:true;index" json:"is_active"`
IsSystem bool `gorm:"default:false;index" json:"is_system"`
RentalEnabled bool `gorm:"default:false" json:"rental_enabled"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
}