EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
add-missing-invoice-line-items.js
Go to the documentation of this file.
1#!/usr/bin/env node
2/**
3 * Add line items to invoices that don't have any
4 * Typically needed for migrated WHMCS invoices
5 */
6
7const pool = require('../services/db');
8
9async function addMissingLineItems() {
10 console.log('[Add Line Items] Starting...\n');
11
12 const client = await pool.connect();
13 let processed = 0;
14 let errors = 0;
15
16 try {
17 // Find all invoices without line items
18 const invoicesResult = await client.query(`
19 SELECT
20 i.invoice_id,
21 i.customer_id,
22 i.total,
23 i.subtotal,
24 i.tax_amount,
25 i.tax_rate,
26 i.currency,
27 i.description
28 FROM invoices i
29 LEFT JOIN invoice_items ii ON i.invoice_id = ii.invoice_id
30 WHERE ii.item_id IS NULL
31 ORDER BY i.invoice_id
32 `);
33
34 console.log(`Found ${invoicesResult.rows.length} invoices without line items\n`);
35
36 for (const invoice of invoicesResult.rows) {
37 try {
38 await client.query('BEGIN');
39
40 // Calculate unit price ex GST
41 const subtotal = parseFloat(invoice.subtotal || invoice.total || 0);
42 const taxAmount = parseFloat(invoice.tax_amount || 0);
43 const total = parseFloat(invoice.total || 0);
44 const taxRate = parseFloat(invoice.tax_rate || 10);
45
46 // Generate description
47 let description = invoice.description || 'Migrated Invoice Services';
48 if (description.includes('Migrated from WHMCS')) {
49 // Extract WHMCS invoice number if available
50 const match = description.match(/Invoice #(\d+)/);
51 if (match) {
52 description = `WHMCS Invoice #${match[1]} - Services`;
53 } else {
54 description = 'Migrated Services';
55 }
56 }
57
58 // Insert single line item representing the full invoice
59 await client.query(`
60 INSERT INTO invoice_items (
61 invoice_id,
62 description,
63 quantity,
64 unit_price,
65 total
66 ) VALUES ($1, $2, $3, $4, $5)
67 `, [
68 invoice.invoice_id,
69 description,
70 1, // quantity
71 subtotal, // unit price ex GST
72 subtotal // total (same as unit_price since quantity=1)
73 ]);
74
75 await client.query('COMMIT');
76 processed++;
77
78 if (processed % 50 === 0) {
79 console.log(`Progress: ${processed}/${invoicesResult.rows.length} invoices processed`);
80 }
81 } catch (err) {
82 await client.query('ROLLBACK');
83 console.error(`Error processing invoice ${invoice.invoice_id}:`, err.message);
84 errors++;
85 }
86 }
87
88 console.log(`\n[Add Line Items] Complete!`);
89 console.log(`✅ Successfully processed: ${processed} invoices`);
90 if (errors > 0) {
91 console.log(`❌ Errors: ${errors} invoices`);
92 }
93
94 } catch (err) {
95 console.error('[Add Line Items] Fatal error:', err);
96 } finally {
97 client.release();
98 await pool.end();
99 }
100}
101
102// Run the script
103addMissingLineItems().catch(console.error);