PSA/server/migrations/20250408215600_populate_tax_regions_from_original.cjs
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

105 lines
3.8 KiB
JavaScript

'use strict';
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = async function (knex) {
// Helper function to generate region_code from original string
const generateRegionCode = (regionString) => {
if (!regionString) return null;
// Simple generation: uppercase, replace non-alphanumeric with hyphen, collapse hyphens
return regionString
.trim()
.toUpperCase()
.replace(/[^A-Z0-9-]+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
};
await knex.transaction(async (trx) => {
// --- Step 1: Collect all distinct original region strings per tenant ---
// Read directly from the original columns before they are dropped by later migrations.
const distinctRegions = new Map(); // Map<tenant, Set<originalRegionString>>
const taxRateRegions = await trx('tax_rates')
.select('tenant', 'region as original_region') // Use original column name
.whereNotNull('region')
.whereRaw("region <> ''")
.distinctOn('tenant', 'region');
const companyRegions = await trx('companies')
.select('tenant', 'tax_region as original_region') // Use original column name
.whereNotNull('tax_region')
.whereRaw("tax_region <> ''")
.distinctOn('tenant', 'tax_region');
const serviceCatalogRegions = await trx('service_catalog')
.select('tenant', 'tax_region as original_region') // Use original column name
.whereNotNull('tax_region')
.whereRaw("tax_region <> ''")
.distinctOn('tenant', 'tax_region');
const processRegions = (records) => {
for (const record of records) {
const tenant = record.tenant;
const originalRegion = record.original_region?.trim();
if (tenant && originalRegion) {
if (!distinctRegions.has(tenant)) {
distinctRegions.set(tenant, new Set());
}
distinctRegions.get(tenant).add(originalRegion);
}
}
};
processRegions(taxRateRegions);
processRegions(companyRegions);
processRegions(serviceCatalogRegions);
// --- Step 2: Populate tax_regions table ---
const regionsToInsert = [];
for (const [tenant, originalRegionsSet] of distinctRegions.entries()) {
for (const originalRegion of originalRegionsSet) {
const regionCode = generateRegionCode(originalRegion);
const regionName = originalRegion; // Use the trimmed original string as name
if (regionCode) {
regionsToInsert.push({
region_code: regionCode,
region_name: regionName,
is_active: true, // Default to active
tenant: tenant,
});
} else {
console.warn(`Generated null region_code for tenant ${tenant}, original region "${originalRegion}"`);
}
}
}
if (regionsToInsert.length > 0) {
console.log(`Populating tax_regions with ${regionsToInsert.length} entries...`);
// Insert into tax_regions, ignoring conflicts on the primary key (tenant, region_code)
// This handles cases where different original strings might normalize to the same code.
// The first one encountered wins.
await trx('tax_regions')
.insert(regionsToInsert)
.onConflict(['tenant', 'region_code'])
.ignore();
} else {
console.log('No distinct regions found to populate tax_regions.');
}
}); // End transaction
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = async function (knex) {
// Basic rollback: Delete all data potentially added by this migration.
// This assumes no manual additions happened between 'up' and 'down'.
// It doesn't restore the previous state, just cleans up what this script did.
console.log('Rolling back tax_regions population. Deleting all entries...');
await knex('tax_regions').delete();
};