Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Docker Public / Build + push Public (test + latest) (push) Waiting to run
Docker Public / Deploy :test → test.food-market.kz (push) Blocked by required conditions
Docker Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
1. Маска телефона: префикс «+7 », оператор не подставляется заранее. Нормализация снимает ведущую «8» и код страны «7» (на 11 цифрах), value хранится каноническим «+7»+national и восстанавливается снятием литерального «+7» (без 8/11-эвристик) — ввод больше не «скачет». Обе копии (public + admin). Проверено на всех вариантах + вставка. 2. Тариф и бесплатный период: Organization.Plan/FreeTrialEndsAt + конфигурация «Бизнес» (Phase50, существующие → «start», дата NULL). Signup сохраняет план и 90 дней; показ в кабинете владельца и в карточке/списке суперадмина. Биллинга/блокировки нет. 3. Ошибки формы: переход к первому полю с ошибкой + фокус; единые сообщения «Укажите email/телефон/название магазина», «Придумайте пароль». 4. «Первые шаги»: кнопка «Скрыть» (возврат из настроек организации); шаг каталога показывает статус копии реестра («загружается…» / «Каталог готов: N товаров — проверьте продажные цены»); ссылка на Forgejo заменена на «Базу знаний». 5. «Новые функции»: не считаем изменения до даты регистрации орг (новой орг баннер скрыт) + русская плюрализация улучшений/исправлений. 6. Карточка товара: «Закупочная цена» видна и в простом режиме (настройка ShowReferencePriceOnProduct), подпись настройки приведена к новой схеме. 7. Копия реестра: первый штрихкод помечается «Основным»; бэкфилл уже скопированных товаров без основного (Phase51). Тесты: PlanSignupTests (3), RegistryCopyPrimaryBarcodeTests (1), MarkupToggleTests (3, регрессия) — зелёные. Phone-нормализация покрыта симуляцией ввода/вставки. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
75 lines
3.9 KiB
C#
75 lines
3.9 KiB
C#
using System.Text.Json;
|
||
using FluentAssertions;
|
||
using foodmarket.Domain.Registry;
|
||
using foodmarket.Infrastructure.Persistence;
|
||
using foodmarket.Infrastructure.Registry;
|
||
using foodmarket.IntegrationTests.Support;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Xunit;
|
||
|
||
namespace foodmarket.IntegrationTests;
|
||
|
||
/// <summary>Копия «Реестра товаров» в каталог магазина должна помечать первый
|
||
/// штрихкод «Основным» (IsPrimary). Товар с единственным штрихкодом раньше
|
||
/// оставался без «Основного» (баг, fix #7).</summary>
|
||
[Collection(ApiCollection.Name)]
|
||
public class RegistryCopyPrimaryBarcodeTests
|
||
{
|
||
private readonly ApiFactory _factory;
|
||
public RegistryCopyPrimaryBarcodeTests(ApiFactory factory) => _factory = factory;
|
||
|
||
private static string RandomBarcode()
|
||
=> string.Concat(Enumerable.Range(0, 13).Select(_ => Random.Shared.Next(0, 10)));
|
||
|
||
[Fact]
|
||
public async Task Copy_sets_single_barcode_as_primary()
|
||
{
|
||
var api = new ApiActor(_factory.CreateClient());
|
||
var slug = $"regcopy-{Guid.NewGuid():N}";
|
||
await api.SignupAndLoginAsync(slug);
|
||
var settings = await api.GetJsonAsync("/api/organization/settings");
|
||
var orgId = Guid.Parse(settings.GetProperty("id").GetString()!);
|
||
|
||
var barcode = RandomBarcode();
|
||
var cardName = $"REG-{slug}";
|
||
|
||
// Готовим карточку реестра с ОДНИМ штрихкодом и запускаем копию напрямую
|
||
// (Hangfire в тестах выключен, поэтому зовём сервис сами).
|
||
using (var scope = _factory.Services.CreateScope())
|
||
{
|
||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||
var unitId = await db.UnitsOfMeasure.AsNoTracking().Select(u => u.Id).FirstAsync();
|
||
db.ProductRegistryCards.Add(new ProductRegistryCard
|
||
{
|
||
Name = cardName,
|
||
UnitOfMeasureId = unitId,
|
||
Cost = 100m,
|
||
PurchasePrice = 100m,
|
||
RetailPrice = 150m,
|
||
Source = RegistryCardSource.Shop,
|
||
Barcodes = new List<ProductRegistryBarcode> { new() { Code = barcode } },
|
||
});
|
||
await db.SaveChangesAsync();
|
||
|
||
// Регистрация выставила RegistryCopyState=1; копия отработает.
|
||
var svc = scope.ServiceProvider.GetRequiredService<ProductRegistryService>();
|
||
await svc.CopyRegistryToOrgAsync(orgId, default);
|
||
}
|
||
|
||
// Находим скопированный товар по штрихкоду и проверяем «Основной».
|
||
var found = await api.GetJsonAsync($"/api/catalog/products?pageSize=50&search={cardName}");
|
||
var items = found.TryGetProperty("items", out var it) ? it : found;
|
||
var product = items.EnumerateArray()
|
||
.FirstOrDefault(p => p.GetProperty("name").GetString() == cardName);
|
||
product.ValueKind.Should().Be(JsonValueKind.Object, "товар из реестра скопирован в каталог");
|
||
|
||
var detail = await api.GetJsonAsync($"/api/catalog/products/{product.GetProperty("id").GetString()}");
|
||
var barcodes = detail.GetProperty("barcodes").EnumerateArray().ToList();
|
||
barcodes.Should().ContainSingle(b => b.GetProperty("code").GetString() == barcode);
|
||
barcodes.Count(b => b.GetProperty("isPrimary").GetBoolean()).Should().Be(1, "ровно один штрихкод — «Основной»");
|
||
barcodes.Single(b => b.GetProperty("code").GetString() == barcode)
|
||
.GetProperty("isPrimary").GetBoolean().Should().BeTrue("единственный штрихкод должен быть «Основным»");
|
||
}
|
||
}
|