Files
telegram-management-system/marketing-agent/services/billing-service/src/routes/invoices.js
你的用户名 237c7802e5
Some checks failed
Deploy / deploy (push) Has been cancelled
Initial commit: Telegram Management System
Full-stack web application for Telegram management
- Frontend: Vue 3 + Vben Admin
- Backend: NestJS
- Features: User management, group broadcast, statistics

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 15:37:50 +08:00

353 lines
7.7 KiB
JavaScript

import express from 'express';
import { invoiceService } from '../services/invoiceService.js';
import { authenticate, requireTenant } from '../middleware/auth.js';
import { validateRequest } from '../middleware/validation.js';
import { body, param, query } from 'express-validator';
import { logger } from '../utils/logger.js';
const router = express.Router();
// Create invoice
router.post('/',
authenticate,
requireTenant,
validateRequest([
body('subscriptionId').optional().isMongoId(),
body('lineItems').isArray(),
body('lineItems.*.description').notEmpty(),
body('lineItems.*.quantity').isInt({ min: 1 }),
body('lineItems.*.unitPrice').isFloat({ min: 0 }),
body('dueDate').optional().isISO8601()
]),
async (req, res) => {
try {
const invoice = await invoiceService.createInvoice(
req.tenantId,
{
...req.body,
customerId: req.tenant.billing?.customerId,
customer: {
name: req.tenant.name,
email: req.tenant.owner.email,
address: req.tenant.billing?.billingAddress
}
}
);
res.status(201).json({
success: true,
invoice
});
} catch (error) {
logger.error('Create invoice error:', error);
res.status(500).json({
success: false,
error: 'Failed to create invoice'
});
}
}
);
// Get invoices
router.get('/',
authenticate,
requireTenant,
validateRequest([
query('status').optional().isIn(['draft', 'open', 'paid', 'void', 'uncollectible']),
query('startDate').optional().isISO8601(),
query('endDate').optional().isISO8601(),
query('limit').optional().isInt({ min: 1, max: 100 })
]),
async (req, res) => {
try {
const invoices = await invoiceService.getInvoices(
req.tenantId,
req.query
);
res.json({
success: true,
invoices
});
} catch (error) {
logger.error('Get invoices error:', error);
res.status(500).json({
success: false,
error: 'Failed to get invoices'
});
}
}
);
// Get unpaid invoices
router.get('/unpaid',
authenticate,
requireTenant,
async (req, res) => {
try {
const invoices = await invoiceService.getUnpaidInvoices(req.tenantId);
res.json({
success: true,
invoices,
totalDue: invoices.reduce((sum, inv) => sum + inv.amountDue, 0)
});
} catch (error) {
logger.error('Get unpaid invoices error:', error);
res.status(500).json({
success: false,
error: 'Failed to get unpaid invoices'
});
}
}
);
// Get overdue invoices
router.get('/overdue',
authenticate,
requireTenant,
async (req, res) => {
try {
const invoices = await invoiceService.getOverdueInvoices(req.tenantId);
res.json({
success: true,
invoices,
totalOverdue: invoices.reduce((sum, inv) => sum + inv.amountDue, 0)
});
} catch (error) {
logger.error('Get overdue invoices error:', error);
res.status(500).json({
success: false,
error: 'Failed to get overdue invoices'
});
}
}
);
// Get invoice by ID
router.get('/:id',
authenticate,
requireTenant,
validateRequest([
param('id').isMongoId()
]),
async (req, res) => {
try {
const invoice = await invoiceService.getInvoice(
req.tenantId,
req.params.id
);
if (!invoice) {
return res.status(404).json({
success: false,
error: 'Invoice not found'
});
}
res.json({
success: true,
invoice
});
} catch (error) {
logger.error('Get invoice error:', error);
res.status(500).json({
success: false,
error: 'Failed to get invoice'
});
}
}
);
// Update invoice (draft only)
router.patch('/:id',
authenticate,
requireTenant,
validateRequest([
param('id').isMongoId(),
body('lineItems').optional().isArray(),
body('dueDate').optional().isISO8601()
]),
async (req, res) => {
try {
const invoice = await invoiceService.updateInvoice(
req.tenantId,
req.params.id,
req.body
);
res.json({
success: true,
invoice
});
} catch (error) {
logger.error('Update invoice error:', error);
res.status(500).json({
success: false,
error: 'Failed to update invoice'
});
}
}
);
// Finalize invoice
router.post('/:id/finalize',
authenticate,
requireTenant,
validateRequest([
param('id').isMongoId()
]),
async (req, res) => {
try {
const invoice = await invoiceService.finalizeInvoice(
req.tenantId,
req.params.id
);
res.json({
success: true,
invoice,
message: 'Invoice finalized and sent'
});
} catch (error) {
logger.error('Finalize invoice error:', error);
res.status(500).json({
success: false,
error: 'Failed to finalize invoice'
});
}
}
);
// Pay invoice
router.post('/:id/pay',
authenticate,
requireTenant,
validateRequest([
param('id').isMongoId(),
body('paymentMethodId').optional().isString()
]),
async (req, res) => {
try {
const invoice = await invoiceService.payInvoice(
req.tenantId,
req.params.id,
{
paymentMethodId: req.body.paymentMethodId
}
);
res.json({
success: true,
invoice,
message: 'Invoice paid successfully'
});
} catch (error) {
logger.error('Pay invoice error:', error);
res.status(500).json({
success: false,
error: 'Failed to pay invoice'
});
}
}
);
// Void invoice
router.post('/:id/void',
authenticate,
requireTenant,
validateRequest([
param('id').isMongoId(),
body('reason').notEmpty()
]),
async (req, res) => {
try {
const invoice = await invoiceService.voidInvoice(
req.tenantId,
req.params.id,
req.body.reason
);
res.json({
success: true,
invoice,
message: 'Invoice voided'
});
} catch (error) {
logger.error('Void invoice error:', error);
res.status(500).json({
success: false,
error: 'Failed to void invoice'
});
}
}
);
// Download invoice PDF
router.get('/:id/pdf',
authenticate,
requireTenant,
validateRequest([
param('id').isMongoId()
]),
async (req, res) => {
try {
const invoice = await invoiceService.getInvoice(
req.tenantId,
req.params.id
);
if (!invoice) {
return res.status(404).json({
success: false,
error: 'Invoice not found'
});
}
// Generate PDF if not exists
if (!invoice.pdfUrl) {
await invoiceService.generatePDF(invoice);
}
// In production, redirect to cloud storage URL
res.redirect(invoice.pdfUrl);
} catch (error) {
logger.error('Download invoice PDF error:', error);
res.status(500).json({
success: false,
error: 'Failed to download invoice'
});
}
}
);
// Send invoice reminder
router.post('/:id/remind',
authenticate,
requireTenant,
validateRequest([
param('id').isMongoId()
]),
async (req, res) => {
try {
await invoiceService.sendInvoiceReminder(
req.tenantId,
req.params.id
);
res.json({
success: true,
message: 'Invoice reminder sent'
});
} catch (error) {
logger.error('Send reminder error:', error);
res.status(500).json({
success: false,
error: 'Failed to send reminder'
});
}
}
);
export default router;