fix: disabling last store/group no longer resets all to enabled

Added store_filters_seeded / group_filters_seeded flags to SyncConfig.
_enabled_*_ids now returns None (all enabled) only before first toggle,
not when the filter table is empty due to all being disabled.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mguschin
2026-05-13 14:36:14 +03:00
parent ebcca2a699
commit 1729ff9b7b
3 changed files with 91 additions and 57 deletions

View File

@@ -0,0 +1,36 @@
"""Add store_filters_seeded and group_filters_seeded to sync_configs."""
revision = "0011"
down_revision = "0010"
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column("sync_configs", sa.Column("store_filters_seeded", sa.Boolean(), nullable=False, server_default="0"))
op.add_column("sync_configs", sa.Column("group_filters_seeded", sa.Boolean(), nullable=False, server_default="0"))
# Mark existing rows as seeded if they already have filters
op.execute("""
UPDATE sync_configs sc
SET store_filters_seeded = 1
WHERE EXISTS (
SELECT 1 FROM sync_filters sf
WHERE sf.sync_config_id = sc.id AND sf.entity_type = 'store'
)
""")
op.execute("""
UPDATE sync_configs sc
SET group_filters_seeded = 1
WHERE EXISTS (
SELECT 1 FROM sync_filters sf
WHERE sf.sync_config_id = sc.id AND sf.entity_type = 'group'
)
""")
def downgrade():
op.drop_column("sync_configs", "group_filters_seeded")
op.drop_column("sync_configs", "store_filters_seeded")

View File

@@ -59,6 +59,8 @@ class SyncConfig(Base):
is_enabled = Column(Boolean, nullable=False, default=False)
evo_mirror_enabled = Column(Boolean, nullable=False, default=False)
vk_mirror_enabled = Column(Boolean, nullable=False, default=False)
store_filters_seeded = Column(Boolean, nullable=False, default=False)
group_filters_seeded = Column(Boolean, nullable=False, default=False)
confirmed_at = Column(DateTime, nullable=True)
price_multiplier = Column(Numeric(10, 4), nullable=False, default=1.0)
created_at = Column(DateTime, nullable=False, server_default=func.now())

View File

@@ -24,29 +24,25 @@ def _get_or_create_sync_config(db: Session, user_id: int) -> SyncConfig:
def _enabled_store_ids(db: Session, user_id: int) -> set[str] | None:
"""Return set of enabled store evotor_ids, or None if no filters set (all enabled)."""
"""Return set of enabled store evotor_ids, or None if filters not yet seeded (all enabled)."""
cfg = db.query(SyncConfig).filter_by(user_id=user_id).first()
if not cfg:
if not cfg or not cfg.store_filters_seeded:
return None
filters = db.query(SyncFilter).filter_by(
sync_config_id=cfg.id, entity_type="store", filter_mode="include"
).all()
if not filters:
return None
return {f.entity_id for f in filters}
def _enabled_group_ids(db: Session, user_id: int, store_evotor_id: str) -> set[str] | None:
"""Return set of enabled group evotor_ids for a store, or None if all enabled."""
"""Return set of enabled group evotor_ids for a store, or None if filters not yet seeded (all enabled)."""
cfg = db.query(SyncConfig).filter_by(user_id=user_id).first()
if not cfg:
if not cfg or not cfg.group_filters_seeded:
return None
filters = db.query(SyncFilter).filter_by(
sync_config_id=cfg.id, entity_type="group", filter_mode="include",
parent_entity_id=store_evotor_id,
).all()
if not filters:
return None
return {f.entity_id for f in filters}
@@ -166,37 +162,20 @@ async def catalog_store_toggle(store_evotor_id: str, request: Request, db: Sessi
cfg = _get_or_create_sync_config(db, user.id)
# If no filters exist yet, that means all stores are implicitly enabled.
# Toggling one store OFF means we create include-filters for all OTHER stores.
existing = db.query(SyncFilter).filter_by(
sync_config_id=cfg.id, entity_type="store", filter_mode="include"
).all()
existing_ids = {f.entity_id for f in existing}
if store_evotor_id in existing_ids:
# Currently enabled → disable: remove its filter
db.query(SyncFilter).filter_by(
sync_config_id=cfg.id, entity_type="store",
entity_id=store_evotor_id, filter_mode="include",
).delete()
else:
if not existing_ids:
# First toggle: seed include-filters for all OTHER stores
all_stores = db.query(CachedStore).filter_by(user_id=user.id).all()
now = datetime.now(timezone.utc).replace(tzinfo=None)
for s in all_stores:
if s.evotor_id == store_evotor_id:
continue
db.add(SyncFilter(
sync_config_id=cfg.id,
entity_type="store",
entity_id=s.evotor_id,
entity_name=s.name,
filter_mode="include",
created_at=now,
))
if cfg.store_filters_seeded:
if store_evotor_id in existing_ids:
# Currently enabled → disable: remove its filter
db.query(SyncFilter).filter_by(
sync_config_id=cfg.id, entity_type="store",
entity_id=store_evotor_id, filter_mode="include",
).delete()
else:
# Re-enable: add its filter back
# Currently disabled → re-enable: add its filter back
db.add(SyncFilter(
sync_config_id=cfg.id,
entity_type="store",
@@ -204,6 +183,22 @@ async def catalog_store_toggle(store_evotor_id: str, request: Request, db: Sessi
filter_mode="include",
created_at=datetime.now(timezone.utc).replace(tzinfo=None),
))
else:
# First toggle ever: seed include-filters for all OTHER stores, mark seeded
all_stores = db.query(CachedStore).filter_by(user_id=user.id).all()
now = datetime.now(timezone.utc).replace(tzinfo=None)
for s in all_stores:
if s.evotor_id == store_evotor_id:
continue
db.add(SyncFilter(
sync_config_id=cfg.id,
entity_type="store",
entity_id=s.evotor_id,
entity_name=s.name,
filter_mode="include",
created_at=now,
))
cfg.store_filters_seeded = True
db.commit()
return RedirectResponse("/catalog/stores", 303)
@@ -227,30 +222,12 @@ async def catalog_group_toggle(
).all()
existing_ids = {f.entity_id for f in existing}
if group_evotor_id in existing_ids:
db.query(SyncFilter).filter_by(
sync_config_id=cfg.id, entity_type="group",
entity_id=group_evotor_id, filter_mode="include",
).delete()
else:
if not existing_ids:
# First toggle: seed include-filters for all OTHER groups in this store
all_groups = db.query(CachedGroup).filter_by(
user_id=user.id, store_evotor_id=store_evotor_id,
).all()
now = datetime.now(timezone.utc).replace(tzinfo=None)
for g in all_groups:
if g.evotor_id == group_evotor_id:
continue
db.add(SyncFilter(
sync_config_id=cfg.id,
entity_type="group",
entity_id=g.evotor_id,
entity_name=g.name,
filter_mode="include",
parent_entity_id=store_evotor_id,
created_at=now,
))
if cfg.group_filters_seeded:
if group_evotor_id in existing_ids:
db.query(SyncFilter).filter_by(
sync_config_id=cfg.id, entity_type="group",
entity_id=group_evotor_id, filter_mode="include",
).delete()
else:
db.add(SyncFilter(
sync_config_id=cfg.id,
@@ -260,6 +237,25 @@ async def catalog_group_toggle(
parent_entity_id=store_evotor_id,
created_at=datetime.now(timezone.utc).replace(tzinfo=None),
))
else:
# First toggle ever: seed include-filters for all OTHER groups in this store, mark seeded
all_groups = db.query(CachedGroup).filter_by(
user_id=user.id, store_evotor_id=store_evotor_id,
).all()
now = datetime.now(timezone.utc).replace(tzinfo=None)
for g in all_groups:
if g.evotor_id == group_evotor_id:
continue
db.add(SyncFilter(
sync_config_id=cfg.id,
entity_type="group",
entity_id=g.evotor_id,
entity_name=g.name,
filter_mode="include",
parent_entity_id=store_evotor_id,
created_at=now,
))
cfg.group_filters_seeded = True
db.commit()
return RedirectResponse(f"/catalog/stores/{store_evotor_id}/groups", 303)