>What would be the best way to make a software multi-companies. I use Stonefield Database Toolkit. Would SDT make things easier?
>
>Overall Feedback?
Hi Denis
I don't know if this is the best way or not but here are some tips on how to enable apps
to run in multicompany(/year) mode.
- Each company have separate folder for it's data
- Each database (DBC+belonging tables ) lives in its own folder (no intermingling with other databases/tables)
Copany databases are organised/stored under some folowing folder structure
addbs(cDataRoot) + 'companies'
- Below this common root folder, each company have it's own subfolder where company database(s) are stored.
.\companies\company1
.\companies\company2
.
Now each company might have one, two or more applications enabled, so you might need to branch even further.
but let's keep it simple here. Since you do not want to have hardcoded paths, actual path to the database have to be
constructed AT RUN TIME according to user login and some company(/perid) selection.
So what comes handy here, is having global object to handle that path construction. If your app already have global
object then you add few properties and methods which are capable of returning proper path based on its global property settings;
So that global object have to have properties like
oGO.CompaniesRoot ='\\NetworkMachine\myappdata\companies'
oGO.CurrentCompany ='Company1'
oGO.CurrentPeriod= ...
oGO.CurrentUser=...
This properties are filled up according to user login and some company/period selection at login time.
After this you need also method which will construct that path and return current path for EACH DATABASE
enabling you to safely open/use multiple databases interchangeably.
See below about global object)
cAccDbc = oGO.Get_Database('MYACC.DBC')
cStkDbc = oGO.Get_Database('MYSTK.DBC')
open database (cAccDbc)
set database to (cAccDbc)
use accs in 0 shared
or
open database (cStkDbc)
set database to (cStkDbc)
use stkitems in 0 shared
This style works very well in prg mode when you open databases prior calling forms and reports. In addition to this
you also need one method which will effectively handle DE environment bug with cursors database paths
if you use some forms/reports which indeed open their data via DE. (Not adviceable but needed sometimes)
So you add this DE fix to gloabal app object as well along with main method which constructs and returns path to
database.
Define class oGlobalApp as ...
CompaniesRoot =''
CurrentCompany =''
CurrentPeriod=''
CurrentUser=''
procedure Get_Database
lParameters cDBC
local cDbcPath
cDbcPath=this.CompaniesRoot + this.CurrentCompany + ...
return cDbcPath
procedure FixDe
lparameters oDataEnvironment
local i,j,db_name,arr_obj(1)
amembers(arr_obj,oDataEnvironment,2)
for i = 1 to alen(arr_obj)
j = oDataEnvironment.&arr_obj(i) .baseclass
if upper(alltrim(j)) = 'CURSOR'
db_name=upper(justfname(allt(oDataEnvironment.&arr_obj(i) .database )))
oDataEnvironment.&arr_obj(i) .database=this.get_database(allt(db_name))
endif
endfor
enddefine
You NEVER hardcode any dbc path, and you ALWAYS open/set databases by calling global object to
return proper run time constructed path. When you have all this in place then multicompany becomes the same thing as working
with single company. You can use multiple copies of the same database containers (DBC) safely, without any worry
that data will get mixed up. Also this aproach give you possibility to freely use any number of DBCs according to
the nature of the data, (eg. sales.dbc,acc.dbc etc) without need to concentrate all tables in one (overbloated)
database container as many people do.
AFAICR SDT was able to update multiple database copies based on common metadata, but I cannot help you much
there, as I use my own DBC upgrade/sync tool for last 10 years or so.
HTH
Sergio