billing invoice
This commit is contained in:
@@ -4,19 +4,13 @@ import crypto from 'crypto'
|
||||
import { authenticator } from 'otplib'
|
||||
import qrcode from 'qrcode'
|
||||
import { getMarketplaceHomepageContent, saveMarketplaceHomepageContent } from '../../services/platformContentService'
|
||||
import { generateInvoicePdf, buildInvoiceNumber } from '../../services/invoicePdfService'
|
||||
import { sendTransactionalEmail } from '../../services/notificationService'
|
||||
import * as presenter from './admin.presenter'
|
||||
import * as repo from './admin.repo'
|
||||
import * as billingService from './admin.billing.service'
|
||||
|
||||
const ADMIN_RESET_TTL_MINUTES = 60
|
||||
|
||||
const PLAN_MONTHLY_AMOUNT: Record<string, number> = {
|
||||
STARTER: 2990,
|
||||
GROWTH: 3990,
|
||||
PRO: 99900,
|
||||
}
|
||||
|
||||
function signAdminToken(adminId: string) {
|
||||
return jwt.sign({ sub: adminId, type: 'admin' }, process.env.JWT_SECRET!, { expiresIn: '8h' })
|
||||
}
|
||||
@@ -226,58 +220,96 @@ export async function updateAdminPermissions(id: string, permissions: any[]) {
|
||||
return presenter.presentAdminUser(admin)
|
||||
}
|
||||
|
||||
export async function getBilling(query: { status?: string; plan?: string; page: number; pageSize: number }) {
|
||||
const [{ data, total }, activeSubscriptions, stats] = await Promise.all([
|
||||
repo.listBillingPage(query),
|
||||
repo.listActiveSubscriptionsForMrr(),
|
||||
repo.getBillingStatusCounts(),
|
||||
])
|
||||
|
||||
const mrr = activeSubscriptions.reduce((acc, subscription) => {
|
||||
const monthlyAmount = PLAN_MONTHLY_AMOUNT[subscription.plan] ?? 0
|
||||
return acc + (subscription.billingPeriod === 'ANNUAL' ? Math.round(monthlyAmount * 10 / 12) : monthlyAmount)
|
||||
}, 0)
|
||||
|
||||
return presenter.presentPaginated(data, total, query.page, query.pageSize, {
|
||||
stats: { mrr, ...stats },
|
||||
})
|
||||
export async function getBilling(query: { q?: string; status?: string; plan?: string; page: number; pageSize: number }) {
|
||||
const { data, total, stats } = await billingService.listBillingAccounts(query)
|
||||
return presenter.presentPaginated(data, total, query.page, query.pageSize, { stats })
|
||||
}
|
||||
|
||||
export async function getCompanyInvoices(companyId: string, query: { page: number; pageSize: number }) {
|
||||
const { data, total } = await repo.listCompanyInvoicesPage(companyId, query)
|
||||
const detail = await billingService.getBillingAccountDetail(companyId)
|
||||
const start = (query.page - 1) * query.pageSize
|
||||
const end = start + query.pageSize
|
||||
const data = detail.invoices.slice(start, end)
|
||||
const total = detail.invoices.length
|
||||
return presenter.presentPaginated(data, total, query.page, query.pageSize)
|
||||
}
|
||||
|
||||
export async function getInvoicePdf(invoiceId: string) {
|
||||
const invoice = await repo.getInvoicePdfRecord(invoiceId)
|
||||
const invoiceNumber = buildInvoiceNumber(invoice.id, invoice.createdAt)
|
||||
const transactionId = invoice.amanpayTransactionId ?? invoice.paypalCaptureId ?? null
|
||||
const pdfBuffer = await generateInvoicePdf({
|
||||
invoiceNumber,
|
||||
issueDate: invoice.createdAt.toISOString(),
|
||||
dueDate: invoice.status === 'PENDING' ? invoice.createdAt.toISOString() : null,
|
||||
company: {
|
||||
name: invoice.company.name,
|
||||
email: invoice.company.email,
|
||||
phone: invoice.company.phone,
|
||||
address: invoice.company.address,
|
||||
},
|
||||
subscription: {
|
||||
plan: invoice.subscription.plan,
|
||||
billingPeriod: invoice.subscription.billingPeriod,
|
||||
currentPeriodStart: invoice.subscription.currentPeriodStart?.toISOString(),
|
||||
currentPeriodEnd: invoice.subscription.currentPeriodEnd?.toISOString(),
|
||||
currency: invoice.subscription.currency,
|
||||
},
|
||||
amount: invoice.amount,
|
||||
currency: invoice.currency,
|
||||
status: invoice.status,
|
||||
paymentProvider: invoice.paymentProvider,
|
||||
transactionId,
|
||||
paidAt: invoice.paidAt?.toISOString(),
|
||||
})
|
||||
return billingService.getInvoicePdf(invoiceId)
|
||||
}
|
||||
|
||||
return { pdfBuffer, invoiceNumber }
|
||||
export function getBillingAccountDetail(companyId: string) {
|
||||
return billingService.getBillingAccountDetail(companyId)
|
||||
}
|
||||
|
||||
export function updateBillingAccount(
|
||||
billingAccountId: string,
|
||||
data: Parameters<typeof billingService.updateBillingAccount>[1],
|
||||
adminId: string,
|
||||
ip?: string,
|
||||
) {
|
||||
return billingService.updateBillingAccount(billingAccountId, data, adminId, ip)
|
||||
}
|
||||
|
||||
export function setDunningPaused(billingAccountId: string, paused: boolean, adminId: string, ip?: string) {
|
||||
return billingService.setDunningPaused(billingAccountId, paused, adminId, ip)
|
||||
}
|
||||
|
||||
export function createBillingInvoice(
|
||||
billingAccountId: string,
|
||||
data: Parameters<typeof billingService.createDraftInvoice>[1],
|
||||
adminId: string,
|
||||
ip?: string,
|
||||
) {
|
||||
return billingService.createDraftInvoice(billingAccountId, data, adminId, ip)
|
||||
}
|
||||
|
||||
export function finalizeBillingInvoice(invoiceId: string, adminId: string, ip?: string) {
|
||||
return billingService.finalizeInvoice(invoiceId, adminId, ip)
|
||||
}
|
||||
|
||||
export function payBillingInvoice(
|
||||
invoiceId: string,
|
||||
data: Parameters<typeof billingService.payInvoice>[1],
|
||||
adminId: string,
|
||||
ip?: string,
|
||||
) {
|
||||
return billingService.payInvoice(invoiceId, data, adminId, ip)
|
||||
}
|
||||
|
||||
export function retryBillingInvoicePayment(
|
||||
invoiceId: string,
|
||||
data: Parameters<typeof billingService.retryInvoicePayment>[1],
|
||||
adminId: string,
|
||||
ip?: string,
|
||||
) {
|
||||
return billingService.retryInvoicePayment(invoiceId, data, adminId, ip)
|
||||
}
|
||||
|
||||
export function voidBillingInvoice(invoiceId: string, reason: string, adminId: string, ip?: string) {
|
||||
return billingService.voidInvoice(invoiceId, reason, adminId, ip)
|
||||
}
|
||||
|
||||
export function markBillingInvoiceUncollectible(invoiceId: string, reason: string, adminId: string, ip?: string) {
|
||||
return billingService.markInvoiceUncollectible(invoiceId, reason, adminId, ip)
|
||||
}
|
||||
|
||||
export function issueBillingCreditNote(
|
||||
invoiceId: string,
|
||||
data: Parameters<typeof billingService.issueCreditNote>[1],
|
||||
adminId: string,
|
||||
ip?: string,
|
||||
) {
|
||||
return billingService.issueCreditNote(invoiceId, data, adminId, ip)
|
||||
}
|
||||
|
||||
export function issueBillingRefund(
|
||||
invoiceId: string,
|
||||
data: Parameters<typeof billingService.issueRefund>[1],
|
||||
adminId: string,
|
||||
ip?: string,
|
||||
) {
|
||||
return billingService.issueRefund(invoiceId, data, adminId, ip)
|
||||
}
|
||||
|
||||
export function getMarketplaceHomepage() {
|
||||
|
||||
Reference in New Issue
Block a user