Merge pull request #2732 from BEDOLAGA-DEV/dev

Dev
This commit is contained in:
c0mrade
2026-03-13 19:11:14 +03:00
committed by GitHub
4 changed files with 38 additions and 26 deletions
+30 -21
View File
@@ -4038,7 +4038,8 @@ async def switch_tariff(
.with_for_update()
.execution_options(populate_existing=True)
)
user.subscription = locked_result.scalar_one()
subscription = locked_result.scalar_one()
user.subscription = subscription
# Use actual_status for correct status check (handles time-based expiration)
actual_status = user.subscription.actual_status
@@ -4238,38 +4239,43 @@ async def switch_tariff(
# Preserve extra purchased devices above the old tariff's base limit
from app.database.crud.subscription import calc_device_limit_on_tariff_switch
user.subscription.tariff_id = new_tariff.id
user.subscription.traffic_limit_gb = new_tariff.traffic_limit_gb
user.subscription.device_limit = calc_device_limit_on_tariff_switch(
current_device_limit=user.subscription.device_limit,
# Re-load subscription to avoid MissingGreenlet from expired lazy relationship
# (subtract_user_balance re-selects User with populate_existing=True which expires relationships)
await db.refresh(user, ['subscription'])
subscription = user.subscription
subscription.tariff_id = new_tariff.id
subscription.traffic_limit_gb = new_tariff.traffic_limit_gb
subscription.device_limit = calc_device_limit_on_tariff_switch(
current_device_limit=subscription.device_limit,
old_tariff_device_limit=current_tariff.device_limit if current_tariff else None,
new_tariff_device_limit=new_tariff.device_limit,
max_device_limit=new_tariff.max_device_limit,
)
user.subscription.connected_squads = new_tariff.allowed_squads or []
subscription.connected_squads = new_tariff.allowed_squads or []
# Reset purchased traffic and delete TrafficPurchase records on tariff switch
from sqlalchemy import delete as sql_delete
from app.database.models import TrafficPurchase
await db.execute(sql_delete(TrafficPurchase).where(TrafficPurchase.subscription_id == user.subscription.id))
user.subscription.purchased_traffic_gb = 0
user.subscription.traffic_reset_at = None
await db.execute(sql_delete(TrafficPurchase).where(TrafficPurchase.subscription_id == subscription.id))
subscription.purchased_traffic_gb = 0
subscription.traffic_reset_at = None
if settings.RESET_TRAFFIC_ON_TARIFF_SWITCH:
user.subscription.traffic_used_gb = 0.0
subscription.traffic_used_gb = 0.0
if switching_to_daily:
# Switching TO daily - reset end_date to 1 day, set last_daily_charge_at
user.subscription.end_date = datetime.now(UTC) + timedelta(days=1)
user.subscription.last_daily_charge_at = datetime.now(UTC)
user.subscription.is_daily_paused = False
subscription.end_date = datetime.now(UTC) + timedelta(days=1)
subscription.last_daily_charge_at = datetime.now(UTC)
subscription.is_daily_paused = False
elif switching_from_daily:
user.subscription.end_date = datetime.now(UTC) + timedelta(days=new_period_days)
user.subscription.is_daily_paused = False
subscription.end_date = datetime.now(UTC) + timedelta(days=new_period_days)
subscription.is_daily_paused = False
user.subscription.updated_at = datetime.now(UTC)
subscription.updated_at = datetime.now(UTC)
await db.commit()
# Emit deferred side-effects after atomic commit
@@ -4287,19 +4293,22 @@ async def switch_tariff(
# Sync with RemnaWave (optionally reset traffic based on admin setting)
should_reset_traffic = settings.RESET_TRAFFIC_ON_TARIFF_SWITCH
# Refresh subscription after commit (all objects are expired)
await db.refresh(subscription)
try:
subscription_service = SubscriptionService()
if getattr(user, 'remnawave_uuid', None):
await subscription_service.update_remnawave_user(
db,
user.subscription,
subscription,
reset_traffic=should_reset_traffic,
reset_reason='смена тарифа',
)
else:
await subscription_service.create_remnawave_user(
db,
user.subscription,
subscription,
reset_traffic=should_reset_traffic,
reset_reason='смена тарифа',
)
@@ -4319,7 +4328,7 @@ async def switch_tariff(
logger.error('Failed to reset devices on tariff switch', error=e)
await db.refresh(user)
await db.refresh(user.subscription)
await db.refresh(subscription)
# Отправляем уведомление админам о смене тарифа
try:
@@ -4334,7 +4343,7 @@ async def switch_tariff(
await notification_service.send_subscription_purchase_notification(
db=db,
user=user,
subscription=user.subscription,
subscription=subscription,
transaction=switch_transaction if upgrade_cost > 0 else None,
period_days=remaining_days if remaining_days > 0 else new_period_days,
was_trial_conversion=False,
@@ -4350,7 +4359,7 @@ async def switch_tariff(
'success': True,
'message': f"Switched from '{old_tariff_name}' to '{new_tariff.name}'"
+ (' (devices reset)' if devices_reset else ''),
'subscription': _subscription_to_response(user.subscription),
'subscription': _subscription_to_response(subscription),
'old_tariff_name': old_tariff_name,
'new_tariff_id': new_tariff.id,
'new_tariff_name': new_tariff.name,
+2 -2
View File
@@ -2323,8 +2323,8 @@ class RemnaWaveService:
async def get_node_user_usage_by_range(self, node_uuid: str, start_date, end_date) -> list[dict[str, Any]]:
try:
async with self.get_api_client() as api:
start_str = start_date.isoformat() + 'Z'
end_str = end_date.isoformat() + 'Z'
start_str = start_date.isoformat().replace('+00:00', 'Z')
end_str = end_date.isoformat().replace('+00:00', 'Z')
params = {'start': start_str, 'end': end_str}
+4 -2
View File
@@ -705,8 +705,10 @@ def create_payment_router(bot: Bot, payment_service: PaymentService) -> APIRoute
if success:
return JSONResponse({'status': 'ok'})
transaction_id = payload.get('transactionId', 'unknown')
logger.error('Platega webhook processing failed: transactionId', transaction_id=transaction_id)
transaction_id = (
payload.get('id') or payload.get('transactionId') or payload.get('transaction_id') or 'unknown'
)
logger.error('Platega webhook processing failed', transaction_id=transaction_id)
return JSONResponse(
{'status': 'error', 'reason': 'not_processed'},
status_code=status.HTTP_400_BAD_REQUEST,
Generated
+2 -1
View File
@@ -551,6 +551,7 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/ab/d26750f2b7242c2b90ea2ad71de70cfcd73a948a49513188a0fc0d6fc15a/greenlet-3.3.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:7ab327905cabb0622adca5971e488064e35115430cec2c35a50fd36e72a315b3", size = 275205, upload-time = "2026-01-23T15:30:24.556Z" },
{ url = "https://files.pythonhosted.org/packages/10/d3/be7d19e8fad7c5a78eeefb2d896a08cd4643e1e90c605c4be3b46264998f/greenlet-3.3.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65be2f026ca6a176f88fb935ee23c18333ccea97048076aef4db1ef5bc0713ac", size = 599284, upload-time = "2026-01-23T16:00:58.584Z" },
{ url = "https://files.pythonhosted.org/packages/ae/21/fe703aaa056fdb0f17e5afd4b5c80195bbdab701208918938bd15b00d39b/greenlet-3.3.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a3ae05b3d225b4155bda56b072ceb09d05e974bc74be6c3fc15463cf69f33fd", size = 610274, upload-time = "2026-01-23T16:05:29.312Z" },
{ url = "https://files.pythonhosted.org/packages/06/00/95df0b6a935103c0452dad2203f5be8377e551b8466a29650c4c5a5af6cc/greenlet-3.3.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:12184c61e5d64268a160226fb4818af4df02cfead8379d7f8b99a56c3a54ff3e", size = 624375, upload-time = "2026-01-23T16:15:55.915Z" },
{ url = "https://files.pythonhosted.org/packages/cb/86/5c6ab23bb3c28c21ed6bebad006515cfe08b04613eb105ca0041fecca852/greenlet-3.3.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6423481193bbbe871313de5fd06a082f2649e7ce6e08015d2a76c1e9186ca5b3", size = 612904, upload-time = "2026-01-23T15:32:52.317Z" },
{ url = "https://files.pythonhosted.org/packages/c2/f3/7949994264e22639e40718c2daf6f6df5169bf48fb038c008a489ec53a50/greenlet-3.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:33a956fe78bbbda82bfc95e128d61129b32d66bcf0a20a1f0c08aa4839ffa951", size = 1567316, upload-time = "2026-01-23T16:04:23.316Z" },
{ url = "https://files.pythonhosted.org/packages/8d/6e/d73c94d13b6465e9f7cd6231c68abde838bb22408596c05d9059830b7872/greenlet-3.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b065d3284be43728dd280f6f9a13990b56470b81be20375a207cdc814a983f2", size = 1636549, upload-time = "2026-01-23T15:33:48.643Z" },
@@ -1115,7 +1116,7 @@ wheels = [
[[package]]
name = "remnawave-bedolaga-telegram-bot"
version = "3.30.0"
version = "3.31.0"
source = { virtual = "." }
dependencies = [
{ name = "aiogram" },