feat(landing,billing): public pricing, global discount, services bar & estimator
Backend
- Add platform-wide global discount (platform_settings: global_discount_percent),
applied centrally in PricingCatalogService.computeTotalsFromDb so it reaches
every real charge (previews, deploys, renewals, upgrades, invoices). Admin
GET/PATCH /billing/settings/global-discount.
- Add unauthenticated PublicPricingController (catalog + calculate) for the
public landing page, returning gross/net and the discount percentage.
- Bill application replicas by the user-selected footprint: app CPU/RAM/storage
now all scale by replica count; the single-replica database stays unscaled.
Frontend
- Landing: Services bar (PaaS active, DBaaS, KaaS/LaaS "coming soon" with
expandable runtime/database menus), transparent Pricing section (per-resource
rate cards with cycle toggle + discount strikethrough), and a cost Estimator
("estimate your package").
- Optional services and the database are priced like runtimes: the estimator
lets users pick their CPU/RAM/storage (and DB type) so the cost scales by need.
- Admin billing: global-discount editor.
- i18n: fa/en strings for services, pricing, estimator and global discount.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { PricingRate } from './entities/pricing-rate.entity';
|
||||
import { AddonRate } from './entities/addon-rate.entity';
|
||||
import { OptionalServiceProfile } from './entities/optional-service-profile.entity';
|
||||
import { OptionalServiceRate } from './entities/optional-service-rate.entity';
|
||||
import { PlatformSetting } from './entities/platform-setting.entity';
|
||||
import {
|
||||
AppRuntime,
|
||||
BillingCycle,
|
||||
@@ -47,6 +48,13 @@ describe('PricingCatalogService', () => {
|
||||
create: jest.fn().mockImplementation((x) => x),
|
||||
};
|
||||
|
||||
const settingsRepo = {
|
||||
find: jest.fn().mockResolvedValue([]),
|
||||
findOne: jest.fn().mockResolvedValue(null),
|
||||
save: jest.fn().mockImplementation((x) => Promise.resolve(x)),
|
||||
create: jest.fn().mockImplementation((x) => x),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
@@ -56,6 +64,7 @@ describe('PricingCatalogService', () => {
|
||||
{ provide: getRepositoryToken(AddonRate), useValue: addonRepo },
|
||||
{ provide: getRepositoryToken(OptionalServiceProfile), useValue: optionalProfileRepo },
|
||||
{ provide: getRepositoryToken(OptionalServiceRate), useValue: optionalRateRepo },
|
||||
{ provide: getRepositoryToken(PlatformSetting), useValue: settingsRepo },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user