|
+ |
Platform
+
+
+
diff --git a/app/modules/billing/templates/billing/merchant/dashboard.html b/app/modules/billing/templates/billing/merchant/dashboard.html
index 28019772..da13c8f6 100644
--- a/app/modules/billing/templates/billing/merchant/dashboard.html
+++ b/app/modules/billing/templates/billing/merchant/dashboard.html
@@ -77,7 +77,7 @@
-
+
·
Renews
diff --git a/app/modules/billing/templates/billing/merchant/subscription-detail.html b/app/modules/billing/templates/billing/merchant/subscription-detail.html
index 3f6a9be8..0ffd9893 100644
--- a/app/modules/billing/templates/billing/merchant/subscription-detail.html
+++ b/app/modules/billing/templates/billing/merchant/subscription-detail.html
@@ -42,7 +42,7 @@
-
+
- Store Code
-
+ Platform
+
Created
@@ -105,16 +105,32 @@
-
-
-
+
+
+
+ Change Plan
+
+
+
+
+
+
+
+ Current Plan
+
+
+
+
+
+
+
@@ -129,7 +145,8 @@ function merchantSubscriptionDetail() {
error: null,
successMessage: null,
subscription: null,
- upgrading: false,
+ availableTiers: [],
+ changingTier: false,
init() {
this.loadSubscription();
@@ -140,8 +157,8 @@ function merchantSubscriptionDetail() {
return match ? decodeURIComponent(match[1]) : null;
},
- getSubscriptionId() {
- // Extract ID from URL: /merchants/billing/subscriptions/{id}
+ getPlatformId() {
+ // Extract platform_id from URL: /merchants/billing/subscriptions/{platform_id}
const parts = window.location.pathname.split('/');
return parts[parts.length - 1];
},
@@ -153,9 +170,9 @@ function merchantSubscriptionDetail() {
return;
}
- const subId = this.getSubscriptionId();
+ const platformId = this.getPlatformId();
try {
- const resp = await fetch(`/api/v1/merchants/billing/subscriptions/${subId}`, {
+ const resp = await fetch(`/api/v1/merchants/billing/subscriptions/${platformId}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
if (resp.status === 401) {
@@ -163,40 +180,68 @@ function merchantSubscriptionDetail() {
return;
}
if (!resp.ok) throw new Error('Failed to load subscription');
- this.subscription = await resp.json();
+ const data = await resp.json();
+ this.subscription = data.subscription || data;
} catch (err) {
console.error('Error:', err);
this.error = 'Failed to load subscription details.';
} finally {
this.loading = false;
}
+
+ // Load available tiers after subscription is loaded
+ await this.loadAvailableTiers(platformId);
},
- async requestUpgrade() {
- this.upgrading = true;
+ async loadAvailableTiers(platformId) {
+ const token = this.getToken();
+ if (!token) return;
+
+ try {
+ const resp = await fetch(`/api/v1/merchants/billing/subscriptions/${platformId}/tiers`, {
+ headers: { 'Authorization': `Bearer ${token}` }
+ });
+ if (!resp.ok) return;
+ const data = await resp.json();
+ this.availableTiers = data.tiers || [];
+ } catch (err) {
+ console.error('Failed to load tiers:', err);
+ }
+ },
+
+ async changeTier(tierCode) {
+ if (!confirm(`Are you sure you want to change your plan to this tier?`)) return;
+
+ this.changingTier = true;
this.error = null;
this.successMessage = null;
const token = this.getToken();
- const subId = this.getSubscriptionId();
+ const platformId = this.getPlatformId();
try {
- const resp = await fetch(`/api/v1/merchants/billing/subscriptions/${subId}/upgrade`, {
+ const resp = await fetch(`/api/v1/merchants/billing/subscriptions/${platformId}/change-tier`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
- }
+ },
+ body: JSON.stringify({ tier_code: tierCode, is_annual: this.subscription?.is_annual || false })
});
if (!resp.ok) {
const data = await resp.json();
- throw new Error(data.detail || 'Upgrade request failed');
+ throw new Error(data.detail || 'Failed to change tier');
}
- this.successMessage = 'Upgrade request submitted. You will be contacted with available options.';
+ const result = await resp.json();
+ this.successMessage = result.message || 'Plan changed successfully.';
+
+ // Reload data
+ this.loading = true;
+ await this.loadSubscription();
} catch (err) {
this.error = err.message;
} finally {
- this.upgrading = false;
+ this.changingTier = false;
}
},
@@ -205,6 +250,14 @@ function merchantSubscriptionDetail() {
return str.charAt(0).toUpperCase() + str.slice(1);
},
+ formatCurrency(cents) {
+ if (cents === null || cents === undefined) return '-';
+ return new Intl.NumberFormat('de-LU', {
+ style: 'currency',
+ currency: 'EUR'
+ }).format(cents / 100);
+ },
+
formatDate(dateStr) {
if (!dateStr) return '-';
return new Date(dateStr).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
diff --git a/app/modules/billing/templates/billing/merchant/subscriptions.html b/app/modules/billing/templates/billing/merchant/subscriptions.html
index 47609f3e..21b1d1cb 100644
--- a/app/modules/billing/templates/billing/merchant/subscriptions.html
+++ b/app/modules/billing/templates/billing/merchant/subscriptions.html
@@ -57,8 +57,7 @@
|
-
-
+
|
| |
-
View Details
diff --git a/tests/unit/services/test_billing_service.py b/app/modules/billing/tests/unit/test_billing_service.py
similarity index 100%
rename from tests/unit/services/test_billing_service.py
rename to app/modules/billing/tests/unit/test_billing_service.py
diff --git a/tests/unit/services/test_capacity_forecast_service.py b/app/modules/billing/tests/unit/test_capacity_forecast_service.py
similarity index 100%
rename from tests/unit/services/test_capacity_forecast_service.py
rename to app/modules/billing/tests/unit/test_capacity_forecast_service.py
diff --git a/tests/unit/services/test_stripe_webhook_handler.py b/app/modules/billing/tests/unit/test_stripe_webhook_handler.py
similarity index 100%
rename from tests/unit/services/test_stripe_webhook_handler.py
rename to app/modules/billing/tests/unit/test_stripe_webhook_handler.py
diff --git a/tests/unit/models/database/test_product.py b/app/modules/catalog/tests/unit/test_product_model.py
similarity index 100%
rename from tests/unit/models/database/test_product.py
rename to app/modules/catalog/tests/unit/test_product_model.py
diff --git a/tests/unit/models/schema/test_product.py b/app/modules/catalog/tests/unit/test_product_schema.py
similarity index 100%
rename from tests/unit/models/schema/test_product.py
rename to app/modules/catalog/tests/unit/test_product_schema.py
diff --git a/tests/unit/services/test_store_product_service.py b/app/modules/catalog/tests/unit/test_store_product_service.py
similarity index 100%
rename from tests/unit/services/test_store_product_service.py
rename to app/modules/catalog/tests/unit/test_store_product_service.py
diff --git a/tests/unit/services/test_admin_customer_service.py b/app/modules/customers/tests/unit/test_admin_customer_service.py
similarity index 100%
rename from tests/unit/services/test_admin_customer_service.py
rename to app/modules/customers/tests/unit/test_admin_customer_service.py
diff --git a/tests/unit/services/test_customer_address_service.py b/app/modules/customers/tests/unit/test_customer_address_service.py
similarity index 100%
rename from tests/unit/services/test_customer_address_service.py
rename to app/modules/customers/tests/unit/test_customer_address_service.py
diff --git a/tests/unit/models/database/test_customer.py b/app/modules/customers/tests/unit/test_customer_model.py
similarity index 100%
rename from tests/unit/models/database/test_customer.py
rename to app/modules/customers/tests/unit/test_customer_model.py
diff --git a/tests/unit/models/schema/test_customer.py b/app/modules/customers/tests/unit/test_customer_schema.py
similarity index 100%
rename from tests/unit/models/schema/test_customer.py
rename to app/modules/customers/tests/unit/test_customer_schema.py
diff --git a/tests/unit/models/database/test_inventory.py b/app/modules/inventory/tests/unit/test_inventory_model.py
similarity index 100%
rename from tests/unit/models/database/test_inventory.py
rename to app/modules/inventory/tests/unit/test_inventory_model.py
diff --git a/tests/unit/models/schema/test_inventory.py b/app/modules/inventory/tests/unit/test_inventory_schema.py
similarity index 100%
rename from tests/unit/models/schema/test_inventory.py
rename to app/modules/inventory/tests/unit/test_inventory_schema.py
diff --git a/tests/integration/api/v1/loyalty/test_storefront_loyalty.py b/app/modules/loyalty/tests/integration/test_storefront_loyalty.py
similarity index 100%
rename from tests/integration/api/v1/loyalty/test_storefront_loyalty.py
rename to app/modules/loyalty/tests/integration/test_storefront_loyalty.py
diff --git a/tests/unit/models/database/test_marketplace_import_job.py b/app/modules/marketplace/tests/unit/test_import_job_model.py
similarity index 100%
rename from tests/unit/models/database/test_marketplace_import_job.py
rename to app/modules/marketplace/tests/unit/test_import_job_model.py
diff --git a/tests/unit/models/schema/test_marketplace_import_job.py b/app/modules/marketplace/tests/unit/test_import_job_schema.py
similarity index 100%
rename from tests/unit/models/schema/test_marketplace_import_job.py
rename to app/modules/marketplace/tests/unit/test_import_job_schema.py
diff --git a/tests/unit/services/test_letzshop_service.py b/app/modules/marketplace/tests/unit/test_letzshop_service.py
similarity index 100%
rename from tests/unit/services/test_letzshop_service.py
rename to app/modules/marketplace/tests/unit/test_letzshop_service.py
diff --git a/tests/unit/services/test_marketplace_product_service.py b/app/modules/marketplace/tests/unit/test_marketplace_product_service.py
similarity index 100%
rename from tests/unit/services/test_marketplace_product_service.py
rename to app/modules/marketplace/tests/unit/test_marketplace_product_service.py
diff --git a/tests/unit/services/test_onboarding_service.py b/app/modules/marketplace/tests/unit/test_onboarding_service.py
similarity index 100%
rename from tests/unit/services/test_onboarding_service.py
rename to app/modules/marketplace/tests/unit/test_onboarding_service.py
diff --git a/tests/unit/models/database/test_marketplace_product.py b/app/modules/marketplace/tests/unit/test_product_model.py
similarity index 100%
rename from tests/unit/models/database/test_marketplace_product.py
rename to app/modules/marketplace/tests/unit/test_product_model.py
diff --git a/tests/unit/services/test_product_service.py b/app/modules/marketplace/tests/unit/test_product_service.py
similarity index 100%
rename from tests/unit/services/test_product_service.py
rename to app/modules/marketplace/tests/unit/test_product_service.py
diff --git a/tests/unit/services/test_email_service.py b/app/modules/messaging/tests/unit/test_email_service.py
similarity index 100%
rename from tests/unit/services/test_email_service.py
rename to app/modules/messaging/tests/unit/test_email_service.py
diff --git a/tests/unit/services/test_message_attachment_service.py b/app/modules/messaging/tests/unit/test_message_attachment_service.py
similarity index 100%
rename from tests/unit/services/test_message_attachment_service.py
rename to app/modules/messaging/tests/unit/test_message_attachment_service.py
diff --git a/tests/unit/services/test_messaging_service.py b/app/modules/messaging/tests/unit/test_messaging_service.py
similarity index 100%
rename from tests/unit/services/test_messaging_service.py
rename to app/modules/messaging/tests/unit/test_messaging_service.py
diff --git a/tests/unit/services/test_customer_order_service.py b/app/modules/orders/tests/unit/test_customer_order_service.py
similarity index 100%
rename from tests/unit/services/test_customer_order_service.py
rename to app/modules/orders/tests/unit/test_customer_order_service.py
diff --git a/tests/unit/services/test_invoice_service.py b/app/modules/orders/tests/unit/test_invoice_service.py
similarity index 100%
rename from tests/unit/services/test_invoice_service.py
rename to app/modules/orders/tests/unit/test_invoice_service.py
diff --git a/tests/unit/models/database/test_order_item_exception.py b/app/modules/orders/tests/unit/test_order_item_exception_model.py
similarity index 100%
rename from tests/unit/models/database/test_order_item_exception.py
rename to app/modules/orders/tests/unit/test_order_item_exception_model.py
diff --git a/tests/unit/services/test_order_metrics_customer.py b/app/modules/orders/tests/unit/test_order_metrics_customer.py
similarity index 100%
rename from tests/unit/services/test_order_metrics_customer.py
rename to app/modules/orders/tests/unit/test_order_metrics_customer.py
diff --git a/tests/unit/models/database/test_order.py b/app/modules/orders/tests/unit/test_order_model.py
similarity index 100%
rename from tests/unit/models/database/test_order.py
rename to app/modules/orders/tests/unit/test_order_model.py
diff --git a/tests/unit/models/schema/test_order.py b/app/modules/orders/tests/unit/test_order_schema.py
similarity index 100%
rename from tests/unit/models/schema/test_order.py
rename to app/modules/orders/tests/unit/test_order_schema.py
diff --git a/app/modules/tenancy/static/admin/js/merchant-detail.js b/app/modules/tenancy/static/admin/js/merchant-detail.js
index 538c5886..e0b3fd8e 100644
--- a/app/modules/tenancy/static/admin/js/merchant-detail.js
+++ b/app/modules/tenancy/static/admin/js/merchant-detail.js
@@ -156,11 +156,16 @@ function adminMerchantDetail() {
}
},
- // Open create subscription modal
+ // Open create subscription modal (only show platforms without existing subscriptions)
async openCreateSubscriptionModal() {
- const firstPlatformId = this.platforms.length > 0 ? this.platforms[0].id : null;
- this.createForm = { platform_id: firstPlatformId, tier_code: 'essential', status: 'trial', trial_days: 14, is_annual: false };
- await this.loadTiers(firstPlatformId);
+ const usedPlatformIds = this.subscriptions.map(e => e.platform_id);
+ const available = this.platforms.filter(p => !usedPlatformIds.includes(p.id));
+ if (available.length === 0) {
+ Utils.showToast('All platforms already have subscriptions', 'info');
+ return;
+ }
+ this.createForm = { platform_id: available[0].id, tier_code: 'essential', status: 'trial', trial_days: 14, is_annual: false };
+ await this.loadTiers(available[0].id);
this.showCreateSubscriptionModal = true;
},
diff --git a/app/modules/tenancy/templates/tenancy/admin/merchant-detail.html b/app/modules/tenancy/templates/tenancy/admin/merchant-detail.html
index 406a6bff..4e4997fc 100644
--- a/app/modules/tenancy/templates/tenancy/admin/merchant-detail.html
+++ b/app/modules/tenancy/templates/tenancy/admin/merchant-detail.html
@@ -320,7 +320,7 @@
diff --git a/tests/integration/api/v1/admin/test_merchant_domains.py b/app/modules/tenancy/tests/integration/test_merchant_domains_api.py
similarity index 100%
rename from tests/integration/api/v1/admin/test_merchant_domains.py
rename to app/modules/tenancy/tests/integration/test_merchant_domains_api.py
diff --git a/tests/unit/models/database/test_admin_platform.py b/app/modules/tenancy/tests/unit/test_admin_platform_model.py
similarity index 100%
rename from tests/unit/models/database/test_admin_platform.py
rename to app/modules/tenancy/tests/unit/test_admin_platform_model.py
diff --git a/tests/unit/services/test_admin_platform_service.py b/app/modules/tenancy/tests/unit/test_admin_platform_service.py
similarity index 100%
rename from tests/unit/services/test_admin_platform_service.py
rename to app/modules/tenancy/tests/unit/test_admin_platform_service.py
diff --git a/tests/unit/models/test_merchant_domain.py b/app/modules/tenancy/tests/unit/test_merchant_domain.py
similarity index 100%
rename from tests/unit/models/test_merchant_domain.py
rename to app/modules/tenancy/tests/unit/test_merchant_domain.py
diff --git a/tests/unit/services/test_merchant_domain_service.py b/app/modules/tenancy/tests/unit/test_merchant_domain_service.py
similarity index 100%
rename from tests/unit/services/test_merchant_domain_service.py
rename to app/modules/tenancy/tests/unit/test_merchant_domain_service.py
diff --git a/tests/unit/services/test_store_domain_service.py b/app/modules/tenancy/tests/unit/test_store_domain_service.py
similarity index 100%
rename from tests/unit/services/test_store_domain_service.py
rename to app/modules/tenancy/tests/unit/test_store_domain_service.py
diff --git a/tests/unit/models/database/test_store.py b/app/modules/tenancy/tests/unit/test_store_model.py
similarity index 100%
rename from tests/unit/models/database/test_store.py
rename to app/modules/tenancy/tests/unit/test_store_model.py
diff --git a/tests/unit/models/schema/test_store.py b/app/modules/tenancy/tests/unit/test_store_schema.py
similarity index 100%
rename from tests/unit/models/schema/test_store.py
rename to app/modules/tenancy/tests/unit/test_store_schema.py
diff --git a/tests/unit/services/test_store_service.py b/app/modules/tenancy/tests/unit/test_store_service.py
similarity index 100%
rename from tests/unit/services/test_store_service.py
rename to app/modules/tenancy/tests/unit/test_store_service.py
diff --git a/tests/unit/services/test_store_team_service.py b/app/modules/tenancy/tests/unit/test_store_team_service.py
similarity index 100%
rename from tests/unit/services/test_store_team_service.py
rename to app/modules/tenancy/tests/unit/test_store_team_service.py
diff --git a/tests/unit/models/database/test_team.py b/app/modules/tenancy/tests/unit/test_team_model.py
similarity index 100%
rename from tests/unit/models/database/test_team.py
rename to app/modules/tenancy/tests/unit/test_team_model.py
diff --git a/tests/unit/services/test_team_service.py b/app/modules/tenancy/tests/unit/test_team_service.py
similarity index 100%
rename from tests/unit/services/test_team_service.py
rename to app/modules/tenancy/tests/unit/test_team_service.py
diff --git a/tests/unit/models/database/test_user.py b/app/modules/tenancy/tests/unit/test_user_model.py
similarity index 100%
rename from tests/unit/models/database/test_user.py
rename to app/modules/tenancy/tests/unit/test_user_model.py
| |