@@ -49,6 +49,11 @@ import { createClient, SupabaseClient, User as SupabaseUser } from '@supabase/su
4949import crypto from 'crypto'
5050import fs from 'fs'
5151import path from 'path'
52+ import { fileURLToPath } from 'url'
53+
54+ // ESM-compatible __dirname
55+ const __filename = fileURLToPath ( import . meta. url )
56+ const __dirname = path . dirname ( __filename )
5257
5358// Load version from API's own package.json
5459const packageJsonPath = path . join ( __dirname , 'package.json' )
@@ -3538,13 +3543,97 @@ export async function buildServer(): Promise<FastifyInstance> {
35383543 }
35393544 }
35403545
3541- // Always save the settings (even if connection failed)
3546+ // Check if this config already exists in saved configs
3547+ // Compare url, database, username, and api_key to detect if it's a new/different config
3548+ const { data : existingConfigs } = await request . supabase !
3549+ . from ( 'odoo_saved_configs' )
3550+ . select ( 'id, url, database, username, api_key_encrypted' )
3551+ . eq ( 'org_id' , request . user . org_id )
3552+ . eq ( 'is_active' , true )
3553+
3554+ // Check if any saved config matches the current settings
3555+ const matchingConfig = existingConfigs ?. find ( config =>
3556+ config . url === normalizedUrl &&
3557+ config . database === database &&
3558+ config . username === username &&
3559+ config . api_key_encrypted === api_key
3560+ )
3561+
3562+ let newConfigId : string | null = null
3563+ let newConfigName : string | null = null
3564+
3565+ // If no matching config exists, create a new saved config automatically
3566+ if ( ! matchingConfig ) {
3567+ // Generate a unique name based on existing configs
3568+ const existingNames = existingConfigs ?. map ( c => c . url ) || [ ]
3569+ let baseName = `${ normalizedUrl . replace ( / ^ h t t p s ? : \/ \/ / , '' ) . split ( '/' ) [ 0 ] } `
3570+ let configName = baseName
3571+ let counter = 1
3572+
3573+ // Check against existing saved config names
3574+ const { data : existingNamedConfigs } = await request . supabase !
3575+ . from ( 'odoo_saved_configs' )
3576+ . select ( 'name' )
3577+ . eq ( 'org_id' , request . user . org_id )
3578+ . eq ( 'is_active' , true )
3579+
3580+ const usedNames = existingNamedConfigs ?. map ( c => c . name ) || [ ]
3581+ while ( usedNames . includes ( configName ) ) {
3582+ counter ++
3583+ configName = `${ baseName } (${ counter } )`
3584+ }
3585+
3586+ // Pick a color based on index
3587+ const colors = [ '#22c55e' , '#3b82f6' , '#8b5cf6' , '#f97316' , '#ec4899' , '#06b6d4' , '#eab308' , '#ef4444' ]
3588+ const colorIndex = ( existingConfigs ?. length || 0 ) % colors . length
3589+
3590+ const { data : newConfig , error : configError } = await request . supabase !
3591+ . from ( 'odoo_saved_configs' )
3592+ . insert ( {
3593+ org_id : request . user . org_id ,
3594+ name : configName ,
3595+ url : normalizedUrl ,
3596+ database,
3597+ username,
3598+ api_key_encrypted : api_key ,
3599+ color : colors [ colorIndex ] ,
3600+ is_active : true ,
3601+ last_tested_at : ! skip_test ? new Date ( ) . toISOString ( ) : null ,
3602+ last_test_success : ! skip_test ? isConnected : null ,
3603+ last_test_error : ! skip_test ? connectionError : null ,
3604+ created_by : request . user . id ,
3605+ updated_by : request . user . id
3606+ } )
3607+ . select ( 'id, name' )
3608+ . single ( )
3609+
3610+ if ( ! configError && newConfig ) {
3611+ newConfigId = newConfig . id
3612+ newConfigName = newConfig . name
3613+ }
3614+ } else {
3615+ // Update the test status on the matching config
3616+ if ( ! skip_test ) {
3617+ await request . supabase !
3618+ . from ( 'odoo_saved_configs' )
3619+ . update ( {
3620+ last_tested_at : new Date ( ) . toISOString ( ) ,
3621+ last_test_success : isConnected ,
3622+ last_test_error : connectionError ,
3623+ updated_by : request . user . id
3624+ } )
3625+ . eq ( 'id' , matchingConfig . id )
3626+ }
3627+ newConfigId = matchingConfig . id
3628+ }
3629+
3630+ // Always save the settings to organization_integrations (even if connection failed)
35423631 const { error } = await request . supabase !
35433632 . from ( 'organization_integrations' )
35443633 . upsert ( {
35453634 org_id : request . user . org_id ,
35463635 integration_type : 'odoo' ,
3547- settings : { url : normalizedUrl , database, username } ,
3636+ settings : { url : normalizedUrl , database, username, config_id : newConfigId , config_name : newConfigName || matchingConfig ?. url } ,
35483637 credentials_encrypted : api_key , // In production, encrypt this
35493638 is_active : true ,
35503639 is_connected : isConnected ,
@@ -3559,11 +3648,17 @@ export async function buildServer(): Promise<FastifyInstance> {
35593648 if ( error ) throw error
35603649
35613650 if ( skip_test ) {
3562- return { success : true , message : 'Odoo credentials saved (connection not tested)' }
3651+ const msg = newConfigName
3652+ ? `Odoo credentials saved as "${ newConfigName } " (connection not tested)`
3653+ : 'Odoo credentials saved (connection not tested)'
3654+ return { success : true , message : msg , new_config : newConfigName ? { id : newConfigId , name : newConfigName } : undefined }
35633655 } else if ( isConnected ) {
3564- return { success : true , message : 'Odoo integration configured and connected!' }
3656+ const msg = newConfigName
3657+ ? `Odoo integration connected and saved as "${ newConfigName } "!`
3658+ : 'Odoo integration configured and connected!'
3659+ return { success : true , message : msg , new_config : newConfigName ? { id : newConfigId , name : newConfigName } : undefined }
35653660 } else {
3566- return { success : true , message : `Credentials saved but connection failed: ${ connectionError } ` , connection_error : connectionError }
3661+ return { success : true , message : `Credentials saved but connection failed: ${ connectionError } ` , connection_error : connectionError , new_config : newConfigName ? { id : newConfigId , name : newConfigName } : undefined }
35673662 }
35683663 } )
35693664
0 commit comments