EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
fix-product-pricing.js
Go to the documentation of this file.
1#!/usr/bin/env node
2/**
3 * Fix Product Pricing from Contract Line Items
4 *
5 * Updates product prices based on actual contract line item prices
6 */
7
8const { Pool } = require('pg');
9
10const pool = new Pool({
11 host: 'rmm-psa-db-do-user-28531160-0.i.db.ondigitalocean.com',
12 port: 25060,
13 database: 'defaultdb',
14 user: 'doadmin',
15 password: 'AVNS_J8RJAmsEwsHFG52_-F2',
16 ssl: { rejectUnauthorized: false }
17});
18
19/**
20 *
21 */
22async function fixProductPricing() {
23 const tenantId = '00000000-0000-0000-0000-000000000001';
24
25 console.log('╔══════════════════════════════════════════════════════════╗');
26 console.log('║ Fixing Product Pricing from Contract Line Items ║');
27 console.log('╚══════════════════════════════════════════════════════════╝\n');
28
29 // Get all products that have associated contract line items
30 const result = await pool.query(`
31 SELECT
32 p.product_id,
33 p.name,
34 p.unit_price as current_price,
35 cli.unit_price as contract_price,
36 cli.description,
37 c.billing_interval
38 FROM products p
39 JOIN contract_line_items cli ON p.product_id = cli.product_id
40 JOIN contracts c ON cli.contract_id = c.contract_id
41 WHERE p.tenant_id = $1
42 ORDER BY p.product_id, cli.unit_price DESC
43 `, [tenantId]);
44
45 console.log(`Found ${result.rows.length} product-contract associations\n`);
46
47 // Group by product and calculate average/most common price
48 const productPrices = {};
49
50 for (const row of result.rows) {
51 if (!productPrices[row.product_id]) {
52 productPrices[row.product_id] = {
53 name: row.name,
54 current_price: parseFloat(row.current_price || 0),
55 prices: []
56 };
57 }
58
59 const price = parseFloat(row.contract_price || 0);
60 if (price > 0) {
61 productPrices[row.product_id].prices.push({
62 price,
63 interval: row.billing_interval
64 });
65 }
66 }
67
68 let updated = 0;
69
70 for (const [productId, data] of Object.entries(productPrices)) {
71 if (data.prices.length === 0) continue;
72
73 // Convert all prices to monthly equivalent
74 const monthlyPrices = data.prices.map(p => {
75 let monthly = p.price;
76 if (p.interval === 'yearly') monthly = p.price / 12;
77 else if (p.interval === 'quarterly') monthly = p.price / 3;
78 else if (p.interval === 'semi_annually') monthly = p.price / 6;
79 return monthly;
80 });
81
82 // Use the most common price (or average if all different)
83 const avgPrice = monthlyPrices.reduce((a, b) => a + b, 0) / monthlyPrices.length;
84 const newPrice = Math.round(avgPrice * 100) / 100; // Round to 2 decimals
85
86 if (newPrice !== data.current_price && newPrice > 0) {
87 await pool.query(
88 'UPDATE products SET unit_price = $1, price_retail = $1, price_ex_tax = $1 WHERE product_id = $2',
89 [newPrice, productId]
90 );
91
92 console.log(`✓ Updated: ${data.name}`);
93 console.log(` Old price: $${data.current_price.toFixed(2)}/month`);
94 console.log(` New price: $${newPrice.toFixed(2)}/month`);
95 console.log(` Based on ${data.prices.length} contract(s)\n`);
96
97 updated++;
98 }
99 }
100
101 console.log(`\n✓ Updated ${updated} product prices\n`);
102
103 // Show current product pricing
104 const products = await pool.query(`
105 SELECT
106 p.product_id,
107 p.name,
108 p.unit_price,
109 COUNT(cli.line_item_id) as contract_count
110 FROM products p
111 LEFT JOIN contract_line_items cli ON p.product_id = cli.product_id
112 WHERE p.tenant_id = $1
113 GROUP BY p.product_id, p.name, p.unit_price
114 HAVING COUNT(cli.line_item_id) > 0
115 ORDER BY p.unit_price DESC
116 `, [tenantId]);
117
118 console.log('UPDATED PRODUCT CATALOG');
119 console.log('=======================\n');
120
121 for (const product of products.rows) {
122 console.log(`${product.name.padEnd(40)} $${parseFloat(product.unit_price).toFixed(2)}/month (${product.contract_count} contracts)`);
123 }
124
125 await pool.end();
126}
127
128fixProductPricing().catch(console.error);