3 * Fix Product Pricing from Contract Line Items
5 * Updates product prices based on actual contract line item prices
8const { Pool } = require('pg');
10const pool = new Pool({
11 host: 'rmm-psa-db-do-user-28531160-0.i.db.ondigitalocean.com',
13 database: 'defaultdb',
15 password: 'AVNS_J8RJAmsEwsHFG52_-F2',
16 ssl: { rejectUnauthorized: false }
22async function fixProductPricing() {
23 const tenantId = '00000000-0000-0000-0000-000000000001';
25 console.log('╔══════════════════════════════════════════════════════════╗');
26 console.log('║ Fixing Product Pricing from Contract Line Items ║');
27 console.log('╚══════════════════════════════════════════════════════════╝\n');
29 // Get all products that have associated contract line items
30 const result = await pool.query(`
34 p.unit_price as current_price,
35 cli.unit_price as contract_price,
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
45 console.log(`Found ${result.rows.length} product-contract associations\n`);
47 // Group by product and calculate average/most common price
48 const productPrices = {};
50 for (const row of result.rows) {
51 if (!productPrices[row.product_id]) {
52 productPrices[row.product_id] = {
54 current_price: parseFloat(row.current_price || 0),
59 const price = parseFloat(row.contract_price || 0);
61 productPrices[row.product_id].prices.push({
63 interval: row.billing_interval
70 for (const [productId, data] of Object.entries(productPrices)) {
71 if (data.prices.length === 0) continue;
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;
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
86 if (newPrice !== data.current_price && newPrice > 0) {
88 'UPDATE products SET unit_price = $1, price_retail = $1, price_ex_tax = $1 WHERE product_id = $2',
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`);
101 console.log(`\n✓ Updated ${updated} product prices\n`);
103 // Show current product pricing
104 const products = await pool.query(`
109 COUNT(cli.line_item_id) as contract_count
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
118 console.log('UPDATED PRODUCT CATALOG');
119 console.log('=======================\n');
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)`);
128fixProductPricing().catch(console.error);