Development - Mago-DACH-Hub/documentation GitHub Wiki

1.1. Create an application

Create a new application using TBLegacy

To generate a new application you can use the tb-legacy tool as follows:

> cd {DevEnv_Path}\Standard\Applications
> tbl n {ApplicationName}

You'll then be asked a few questions.

  • {ApplicationName} must be a valid non-existing folder name, only containing alphanumeric characters, '_' or '-'.
  • The name of your organisation has to fit the name set at microarea.it
  • The description is the name that's shown in Mago
  • The 4-chars short name is later used to create the license / .csm file at microarea.it

For further information, check out the TBLegacy wiki.

▌Example▐
PS C:\DEV\dev35\Standard\Applications> tbl n Comics

Welcome to the TBLegacy generator!
? What is the name of your organization?	Zucchetti
? Give your app a description			Comics App
? Application version				1.0.0.0
? Is it a codeless application?			No
? Re-use ERP precompiled headers?		Yes
? Name of the first module			Comics
? Description of the module			Comics module
? Module 4-chars short name			CMIX
? Name of the first library			ComicsDbl

    force ..\..\..\..\Users\{User}\.yo-rc-global.json
   create Comics\Application.config
   create Comics\Comics.sln
   create Comics\Comics.props
   create Comics\Solutions\Comics.Solution.xml
   create Comics\Solutions\Comics.Solution.Brand.xml
   create Comics\Solutions\Modules\Comics.xml
   create Comics\Fragments\Comics.xml
   create Comics\Comics\Module.config
   create Comics\Comics\DatabaseScript\Create\CreateInfo.xml
   create Comics\Comics\DatabaseScript\Upgrade\UpgradeInfo.xml
   create Comics\Comics\Menu\Comics.menu
   create Comics\Comics\Files\Images\CompanyLogo.png
   create Comics\Comics\Files\Images\SplashMenuManager.jpg
   create Comics\Comics\Files\Images\Comics.png
   create Comics\Comics\ModuleObjects\AddOnDatabaseObjects.xml
   create Comics\Comics\ModuleObjects\ClientDocumentObjects.xml
   create Comics\Comics\ModuleObjects\DatabaseObjects.xml
   create Comics\Comics\ModuleObjects\DocumentObjects.xml
   create Comics\Comics\ModuleObjects\Enums.xml
   create Comics\Comics\ModuleObjects\EventHandlerObjects.xml
   create Comics\Comics\EFCore\EFSchemaObjects.xml
   create Comics\Comics\ComicsDbl\beginh.dex
   create Comics\Comics\ComicsDbl\endh.dex
   create Comics\Comics\ComicsDbl\stdafx.cpp
   create Comics\Comics\ComicsDbl\stdafx.h
   create Comics\Comics\ComicsDbl\ComicsDbl.vcxproj
   create Comics\Comics\ComicsDbl\ComicsDbl.cpp
   create Comics\Comics\ComicsDbl\ComicsDblInterface.cpp

No change to package.json was detected. No package manager install will be executed.

Register your application with Microarea

  • Register the application in the Microarea portal
  • Crypt the default module definition ({DevEnv_Path}\Standard\Applications\{ApplicationName}\Solutions\Modules\{DefaultModuleName}.xml) Download the returned .csm file into the same folder.
  • Generate at least one serial number for your application, from the specific page of the portal

    Alternatively,
    open {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\Module.config
    and change the deployment policy from full to base to be able to license the module without the need of a serial number.

    e.g. <Library name="ExampleDbl" sourcefolder="Dbl" deploymentpolicy="base" />

  • Restart IIS (e.g. with iisreset) to make the LoginManager reload the list of available applications
  • Launch the Administration Console (/Apps/AdministrationConsole/Release/) and activate the new application
  • Upgrade the companies databases; even if there are no new tables present, it is needed to "brand" the DB with the new application

1.2. Create a module

Alternatively, if you don't want a new application, but only a new module in an existing application, us TBLegacy like this:

> cd {DevEnv_Path}\Standard\Applications\{ApplicationName}
> tbl m {ModuleName}

You'll then be asked two questions.

  • {ModuleName} must be a valid non-existing folder name, only containing alphanumeric characters, '_' or '-'.
  • The 4-chars short name is later used to create the license / .csm file at microarea.it

For further information, check out the TBLegacy wiki.

▌Example▐
PS C:\DEV\dev35\Standard\Applications\Comics> tbl m Manga

Welcome to the TBLegacy Module generator!
You are about to add a module to the Comics application.
? Description of the module	Manga module
? Module 4-chars short name	MNGA

    force ..\..\..\..\..\Users\{User}\.yo-rc-global.json
   create Solutions\Modules\Manga.xml
    force Solutions\Comics.Solution.xml
   create Fragments\Manga.xml
   create Manga\Module.config
   create Manga\DatabaseScript\Create\CreateInfo.xml
   create Manga\DatabaseScript\Upgrade\UpgradeInfo.xml
   create Manga\Menu\Manga.menu
   create Manga\Files\Images\CompanyLogo.png
   create Manga\Files\Images\SplashMenuManager.jpg
   create Manga\Files\Images\Manga.png
   create Manga\ModuleObjects\AddOnDatabaseObjects.xml
   create Manga\ModuleObjects\ClientDocumentObjects.xml
   create Manga\ModuleObjects\DatabaseObjects.xml
   create Manga\ModuleObjects\DocumentObjects.xml
   create Manga\ModuleObjects\Enums.xml
   create Manga\ModuleObjects\EventHandlerObjects.xml
   create Manga\EFCore\EFSchemaObjects.xml

No change to package.json was detected. No package manager install will be executed.

2. Create a database table

2.1.1. Create a new table

The first step when creating an application is to create the necessary database table.
All databases are defined and hosted in the Dbl library of your application.
To create a new table you use the tblegacy tool like this:

> cd {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}
> tbl t {TableName}

You'll then be asked a few questions.

  • {TableName} is the physical name of the table used for the DB (w/ two-lettered prefix - e.g. "MA_" or "RM_"). It must be a non-existing valid name and cannot include spaces or special characters.
  • You can choose a TableType among master, master/detail or slave ("codeless" only). In the master/detail case, actually a pair of tables are generated; one intended to be a header, the other to contain lines (same name, but with a "Detail" suffix)
  • You have the option to "scaffold default fields" which might be recommended for beginners. This options will generate some fields inside the table, with predefined names, types and lenghts.

For further information, check out the TBLegacy wiki.

▌Example▐
PS C:\DEV\dev35\Standard\Applications\Comics\comics> tbl t CO_Collections
Welcome to the TBLegacy Table generator!
You are about to add a table to the comics module of the Comics application.
? Is it a codeless table?               No
? Which is the hosting library ?        ComicsDbl
? Which kind of table you want:         master
? Scaffold some default fields (Y)
      or start with no fields (n)?      Yes

    force ..\..\..\..\..\Users\{User}\.yo-rc-global.json
   create DatabaseScript\Create\All\CO_Collections.sql
    force DatabaseScript\Create\CreateInfo.xml
   create ComicsDbl\TCollections.h
   create ComicsDbl\TCollections.cpp
    force ComicsDbl\DblInterface.cpp
    force ComicsDbl\ComicsDbl.vcxproj
    force ModuleObjects\DatabaseObjects.xml
    force EFCore\EFSchemaObjects.xml

No change to package.json was detected. No package manager install will be executed.

Browse to {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\DatabaseScript\Create. Here you'll find the CreateInfo.xml file, which defines the creation process of your module and the steps in which the tables will be created with their respective SQL script.

<!-- Example → CreateInfo.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<CreateInfo>
	<ModuleInfo name="Comics" />
	<Level1>
		<Step numstep="1" script="CO_Collections.sql" />
		<Step numstep="2" script="CO_Boxes.sql" />
		<Step numstep="3" script="Alter_MA_Items.sql" />
		<Step numstep="4" script="Alter_MA_InventoryReasons.sql" />
	</Level1>
	<Level2 />
</CreateInfo>

In this example, there are SQL scripts with a "Alter_" prefix, which are normally only used, when you upgarde your module or your module adds on to an existing module. Refer to this wiki's AddOns section.

TBLegacy will also have created a SQL script to create your DB table. To update this script with your wanted fields, browse to {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\DatabaseScript\Create\All. In addition, in case your module will be used with a system using Oracle in the future, you'll need to create a SQL script with the same name under the {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\DatabaseScript\Create\Oracle folder, which you might create yourself.

-- Example → ...\DatabaseScript\Create\All\CO_Collections.sql
if not exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[CO_Collections]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN
CREATE TABLE [dbo].[CO_Collections] (
    [Collection]    [varchar]   (20)    NOT NULL,
    [Description]   [varchar]   (32)    NULL CONSTRAINT DF_Collections_Descri_00    DEFAULT (''),
    [Notes]         [varchar]   (128)   NULL CONSTRAINT DF_Collections_Note_00      DEFAULT (''),
    [Disabled]      [char]      (1)     NULL CONSTRAINT DF_Collections_Disab_00     DEFAULT ('0'),
    CONSTRAINT [PK_Collections] PRIMARY KEY NONCLUSTERED (
        [Collection]
    ) ON [PRIMARY]
) ON [PRIMARY]
END
GO
-- Example → ...\DatabaseScript\Create\Oracle\CO_Collections.sql
CREATE TABLE "CO_COLLECTIONS" (
    "COLLECTION"    VARCHAR2    (20)    NOT NULL,
    "DESCRIPTION"   VARCHAR2    (128)   DEFAULT (''),
    "NOTES"         VARCHAR2    (32)    DEFAULT (''),
    "DISABLED"		CHAR    (1)     DEFAULT ('0'),
    CONSTRAINT "PK_COLLECTIONS" PRIMARY KEY (
        "COLLECTION"
    )
)
GO

For help regarding SQL syntax, use e.g. w3schools. You can also note down the fields and their respective type and length of your table, because you'll need it again later in development.


TBLegacy also updates the DatabaseObjects.xml found at {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\ModuleObjects, which only needs the definition of the namespace and whether it's a mastertable or not.

<!-- Example → DatabaseObjects.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<DatabaseObjects>
    <Signature>Comics</Signature>
    <Release>2</Release>
    <Tables>
        <Table namespace="Comics.Comics.ComicsDbl.CO_Collections" mastertable="true" >
            <Create release="1" createstep="1" />
        </Table>
        <Table namespace="Comics.Comics.ComicsDbl.CO_Boxes" mastertable="true" >
            <Create release="2" createstep="2" />
        </Table>
        <Table namespace="Comics.Comics.ComicsDbl.CO_CollectionsBox" mastertable="true" >
            <Create release="2" createstep="3" />
        </Table>
        <Table namespace="Comics.Comics.ComicsDbl.CO_CollectionsBoxDetails" >
            <Create release="2" createstep="4" />
        </Table>
    </Tables>
</DatabaseObjects>

As well as the EFSchemaObjects.xml found at {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\EFCore, which is important for MagoCloud development and thus needs more defined information like columns, their type and such.

// TODO: Move ↓ to MagoCloud development

<!-- Simplified Example → EFSchemaObjects.xml -->
<?xml version="1.0" encoding="utf-8"?>
<DatabaseObjects version="1">
    <Signature>Comics</Signature>
    <Release>2</Release>
    <Tables>
        <Table namespace="Comics.Comics.ComicsDbl.CO_Collections" mastertable="true">
            <DocumentationInfo localizable_1="true">Collections master table</DocumentationInfo>
            <Create release="1" createstep="1" />
            <Columns>
                <Column>
                    <SchemaInfo localizable_1="true" type="String" defaultvalue="" length="20">Collection</SchemaInfo>
                    <DocumentationInfo localizable_1="true" mandatory="true">Collections code</DocumentationInfo>
                </Column>
                <Column>
                    <SchemaInfo localizable_1="true" type="String" defaultvalue="" length="128">Description</SchemaInfo>
                    <DocumentationInfo localizable_1="true">Collections description</DocumentationInfo>
                </Column>
                <Column>
                    <SchemaInfo localizable_1="true" type="String" defaultvalue="" length="32">Notes</SchemaInfo>
                    <DocumentationInfo localizable_1="true">Collections notes</DocumentationInfo>
                </Column>
                <Column>
                    <SchemaInfo localizable_1="true" type="String" defaultvalue="" length="1">Disabled</SchemaInfo>
                    <DocumentationInfo localizable_1="true">Collections disabled</DocumentationInfo>
                </Column>
            </Columns>
            <PrimaryKey name="PK_Collections" type="NONCLUSTERED">
                <Segments>Collection</Segments>
            </PrimaryKey>
            <Indexes/>
        </Table>
        <Table namespace="Comics.Comics.ComicsDbl.CO_Boxes" mastertable="true">
            <DocumentationInfo localizable_1="true">Boxes master table</DocumentationInfo>
            <Create release="2" createstep="2" />
            [...]
        </Table>
        <Table namespace="Comics.Comics.ComicsDbl.CO_CollectionsBox" mastertable="true">
            <DocumentationInfo localizable_1="true">CollectionsBox header</DocumentationInfo>
            <Create release="2" createstep="3" />
            <Columns>
                [...]
            </Columns>
            <PrimaryKey name="PK_CollectionsBox" type="NONCLUSTERED">
                <Segments>DocID</Segments>
            </PrimaryKey>
            <Indexes/>
        </Table>
        <Table namespace="Comics.Comics.ComicsDbl.CO_CollectionsBoxDetails">
            <DocumentationInfo localizable_1="true">CollectionsBox lines</DocumentationInfo>
            <Create release="2" createstep="4" />
            <Columns>
                <Column>
                    <SchemaInfo localizable_1="true" type="Long" defaultvalue="0" length="0">DocID</SchemaInfo>
                    <DocumentationInfo localizable_1="true" mandatory="true">Document ID</DocumentationInfo>
                </Column>
                <Column>
                    <SchemaInfo localizable_1="true" type="Long" defaultvalue="0" length="0">DocSubID</SchemaInfo>
                    <DocumentationInfo localizable_1="true">Line Sub-ID</DocumentationInfo>
                </Column>
                <Column>
                    <SchemaInfo localizable_1="true" type="String" defaultvalue="" length="10">Code</SchemaInfo>
                    <DocumentationInfo localizable_1="true" mandatory="true"></DocumentationInfo>
                </Column>
                <Column>
                    <SchemaInfo localizable_1="true" type="String" defaultvalue="" length="128">Description</SchemaInfo>
                    <DocumentationInfo localizable_1="true"></DocumentationInfo>
                </Column>
            </Columns>
            <PrimaryKey name="PK_CollectionsBoxDetails" type="NONCLUSTERED">
                <Segments>DocID, DocSubID</Segments>
            </PrimaryKey>
            <ForeignKeys>
                <ForeignKey name="FK_CollectionsBoxDetails_CollectionsBox_00" on="CollectionsBox" onns="Comics.Comics.ComicsDbl.CO_CollectionsBox">
                    <FKSegments>DocID</FKSegments>
                    <PKSegments>DocID</PKSegments>
                </ForeignKey>
            </ForeignKeys>
            <Indexes/>
        </Table>
    </Tables>
</DatabaseObjects>

Your next step is starting the Mago AdministrationConsole and create/update database structure on your company to actually create the tables in your database.

2.1.2. Update an existing table

In case you don't develop a module from the ground up, it might be necessery to update a module by creating an additional table or alter an already existing table.

The steps in 2.1.1. are still important, so make sure all your data is correct regarding your table, its columns and the release number in the DatabaseObjects.xml, EFSchemaObjects.xml and additionally in the Interface.cpp of your hosting library.

/* Simplified Example → ...\ComicsDbl\ComicsDblInterface.cpp*/
BEGIN_ADDON_INTERFACE()         →   BEGIN_ADDON_INTERFACE()
    DATABASE_RELEASE(1)         →   	DATABASE_RELEASE(2)
    //...                       →       //...
END_ADDON_INTERFACE()           →   END_ADDON_INTERFACE()

When you actually have to update an existing table, create a SQL script in {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\DatabaseScript\Upgrade\All\Release_{ReleaseNo}. The naming convention for these update scripts is Alter_{TableName}. Again, for sake, you need to provide a Oracle script in in {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\DatabaseScript\Upgrade\Oracle\Release_{ReleaseNo} as well.

-- Example → ...\DatabaseScript\Upgrade\All\Release_2\Alter_COCollections.sql
IF EXISTS ( SELECT dbo.syscolumns.name
            FROM dbo.syscolumns, dbo.sysobjects
            WHERE dbo.sysobjects.name = 'CO_Collections'
              AND dbo.sysobjects.id = dbo.syscolumns.id
              AND dbo.syscolumns.name = 'Notes')
BEGIN
ALTER TABLE [dbo].[CO_Collections]
   ALTER COLUMN [Notes] [varchar] (256)
END
GO
-- Example → ...\DatabaseScript\Upgrade\Oracle\Release_2\Alter_COCollections.sql
ALTER TABLE "CO_COLLECTIONS"
MODIFY "NOTES" VARCHAR2 (256)
GO

Lastly, add a "DBRel" node in {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\DatabaseScript\Upgrade\UpgradeInfo.xml.

<!-- Example → UpgradeInfo.xml -->
<?xml version="1.0" encoding="utf-8"?>
<UpgradeInfo>
    <ModuleInfo name="Comics" />
    <DBRel numrel="2">
        <Level1>
            <Step numstep="1" script="CO_Collections.sql" />
        </Level1>
    </DBRel>
</UpgradeInfo>

It's important that numrel is the same as {ReleaseNo} in the folder name of your update scripts.

Your next step is starting the Mago AdministrationConsole and create/update database structure on your company to actually create the tables in your database.


2.2. Changes inside the module solution

The TBLegacy Table Generator created a .cpp and a .h file for your table (that still need to be modified so that they include the defined columns of your table) and registers the class into your Library Interface.

/* Example → ComicsDblInterface.cpp */
BEGIN_TABLES()
    BEGIN_REGISTER_TABLES()
        REGISTER_TABLE		(TCollections)
    END_REGISTER_TABLES()
END_TABLES()

In the .h file of your class, you have to define your columns as variables. The naming convention is f_{ColumnName}.

/* Example → TCollections.h */
#pragma once
#include "beginh.dex"

/////////////////////////////////////////////////////////////////////////////
class TB_EXPORT TCollections : public SqlRecord
{
    DECLARE_DYNCREATE(TCollections) 

public:
    DataStr     f_Collection;
    DataStr     f_Description;
    DataStr     f_Notes;
    DataBool    f_Disabled;

public:
    TCollections(BOOL bCallInit = TRUE);

public:
    virtual void	BindRecord();

public:
    static  LPCTSTR  GetStaticName();
};

#include "endh.dex"

These "Data" types are special types defined and used by Microarea that are all derived from DataObj. If you have doubt of what type to use, search for a already existing database column with the same type as yours in the ERP.sln.

Possible datatypes include amongst others:
DataBool    ←   boolean         // represented with char(1) on the DB
DataDate    ←   datetime
DataDbl     ←   double / float  // represented with float on the DB (TODO: why? what's special for Dbl?)
DataEnum    ←   enumartions     // represented with int on the DB (like DocumentType, LineType and such)
DataGuid    ←   guids           // 'Globally Unique Identifier' (or 'Universally Unique Identifier'); probably represented with uniqueidentifier on the DB
DataInt     ←   integer         // represented with int or smallint on the DB
DataLng     ←   long            // represented with int on the DB (used for Ids)
DataMon     ←   double / float  // represented with money on the DB
DataObj     ←   object
DataPerc    ←   double / float  // represented with float on the DB, specifically used to represent a percentage number (like Discount)
DataQty     ←   double / float  // represented with float on the DB
DataStr     ←   string          // represented with varchar on the DB (used for Ids)
DataText    ←   string          // represented with ntext on the DB (i.e. long strings)

After you're done with the header file, continue with the cpp portion of your class.
/* Example → TCollections.cpp */
#include "stdafx.h"             

#include "TCollections.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
//                      class TCollections Implementation
/////////////////////////////////////////////////////////////////////////////
//
//=============================================================================
IMPLEMENT_DYNCREATE(TCollections, SqlRecord) 

//-----------------------------------------------------------------------------
TCollections::TCollections(BOOL bCallInit)
    :
    SqlRecord	(GetStaticName()),
    f_Disabled(FALSE)
{
    f_Collection.SetUpperCase();

    BindRecord();	
    if (bCallInit) Init();
}

//-----------------------------------------------------------------------------
void TCollections::BindRecord()
{
    BEGIN_BIND_DATA();
        BIND_DATA   (_NS_FLD("Collection"),     f_Collection);
        BIND_DATA   (_NS_FLD("Description"),    f_Description);
        BIND_DATA   (_NS_FLD("Notes"),          f_Notes);
        BIND_DATA   (_NS_FLD("Disabled"),       f_Disabled);
        BIND_TB_GUID();
    END_BIND_DATA();    
}

//-----------------------------------------------------------------------------
LPCTSTR TCollections::GetStaticName() { return _NS_TBL("CO_Collections"); }

In the constructor, you can initialise f_Disabled with FALSE since it's a boolean that defined to default to 0. It's also courtesy to set your Primary Key (in this case f_Collection) to uppercase to improve the comparablity and minimize errors. In addition to that, you have to bind your table columns to your in the header file defined variables in the BindRecord() method.

3. Creating a Document

3.1. Create hosting libraries

To create a Document we need 2 libraries:

  • Document library └ houses the code for the documents (like logic checks, click commands or database lookups)
  • Components library

To create each type of library you can use the TBLegacy-tool like this:

> cd {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}
> tbl l {LibraryName}

You'll then be asked a question.

  • {LibraryName} is used to name its containing folder. It must be a valid non-existing folder name, only containing alphanumeric characters, '_' or '-'.
  • As the usual case is that the library makes use of some resources defined in ERP, it may be useful to re-use ERP precompiled headers to save compilation time. (Note KS: when trying with Comics example, this option didn't make a difference in code, as far as I could see)

For further information, check out the TBLegacy wiki.

▌Example → Documents Library▐
PS C:\DEV\dev35\Standard\Applications\Comics\comics> tbl l ComicsDocuments
Welcome to the TBLegacy Table generator!
You are about to add a library to the Comics module of the Comics application.
? Re-Use ERP precompiled headers? (y/N)     No

    force ..\..\..\..\..\Users\{User}\.yo-rc-global.json
   create ComicsDocuments\beginh.dex
   create ComicsDocuments\endh.dex
   create ComicsDocuments\stdafx.cpp
   create ComicsDocuments\stdafx.h
   create ComicsDocuments\ComicsDocuments.vcxproj
   create ComicsDocuments\ComicsDocuments.cpp
   create ComicsDocuments\ComicsDocumentsInterface.cpp
    force Module.config
    force ../Comics.sln

No change to package.json was detected. No package manager install will be executed.
▌Example → Components Library▐
PS C:\DEV\dev35\Standard\Applications\Comics\comics> tbl l ComicsComponents
Welcome to the TBLegacy Table generator!
You are about to add a library to the comics module of the Comics application.
? Re-Use ERP precompiled headers? (y/N)     No

    force ..\..\..\..\..\Users\{User}\.yo-rc-global.json
   create ComicsComponents\beginh.dex
   create ComicsComponents\endh.dex
   create ComicsComponents\stdafx.cpp
   create ComicsComponents\stdafx.h
   create ComicsComponents\ComicsComponents.vcxproj
   create ComicsComponents\ComicsComponents.cpp
   create ComicsComponents\ComicsComponentsbInterface.cpp
    force Module.config
    force ../Comics.sln

No change to package.json was detected. No package manager install will be executed.

3.2. Create a new document

After we created those two libraries we can also create a document through TBLegacy

To create each type of library you can use the TBLegacy-tool like this:

> cd {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}
> tbl d {DocumentName}

You'll then be asked a few questions.

  • It is checked that a document with {DocumentName} does not exist (in the ModuleObjects folder).
  • A codeless document is defined via metadata only, there is no C++ wrapper class. A codeless document can exists in a non-codeless application, but the vice-versa is not supported out-of-the-box.
  • The Hosting Library should be the Documents Library to host the document.
  • It allows to choose among master and master/detail. The first generates a document containing just a header (DBTMaster). The second generates a document with a header (DBTMaster) and a detail (DBTSlaveBuffered).
  • If the document is a master/detail type, the name of the details table is inferred by adding Details to the master name.
  • Library for ADM definition should be the Components Library.
  • If you don't scaffold default UI, the UI is left empty, with just a containing frame, a default view and an empty tile.

For further information, check out the TBLegacy wiki.

▌Example▐
C:\DEV\dev35\Standard\Applications\Comics\Comics> tbl d Collections

Welcome to the TBLegacy Document generator!
You are about to add a document to the Comics module of the Comics application.
? Is it a codeless document?                        No
? Which is the hosting library ?                    ComicsDocuments
? Which kind of document you want:                  master
? Set the main form title                           Collections document
? Which library contains the table definition ?     ComicsDbl
? Which is the master table?                        TCollections
? What is the master table phisical name?           CO_Collections
? Which library will contain the ADM definition ?   ComicsComponents
? Scaffold some default UI (Y)
                    or start with empty UI (n)?     Yes

    force ..\..\..\..\..\..\Users\schkev\.yo-rc-global.json
   create ComicsComponents\ADMCollections.h
   create ComicsComponents\ADMCollections.cpp
    force ComicsComponents\ComicsComponents.vcxproj
   create ComicsDocuments\DCollections.h
   create ComicsDocuments\DCollections.cpp
   create ComicsDocuments\UICollections.hjson
    force ComicsDocuments\ComicsDocumentsInterface.cpp
    force ComicsDocuments\ComicsDocuments.vcxproj
   create ModuleObjects\Collections\Description\Dbts.xml
   create ModuleObjects\Collections\Description\Defaults.xml
   create ModuleObjects\Collections\Description\Document.xml
   create ModuleObjects\Collections\Description\ExternalReferences.xml
   create ModuleObjects\Collections\JsonForms\IDD_COLLECTIONS.tbjson
   create ModuleObjects\Collections\JsonForms\IDD_TD_COLLECTIONS_MAIN.tbjson
   create ModuleObjects\Collections\JsonForms\IDD_COLLECTIONS_VIEW.tbjson
   create ModuleObjects\Collections\JsonForms\IDD_COLLECTIONS.hjson
   create ModuleObjects\Collections\JsonForms\IDD_TD_COLLECTIONS_MAIN.hjson
   create ModuleObjects\Collections\JsonForms\IDD_COLLECTIONS_VIEW.hjson
    force ModuleObjects\DocumentObjects.xml
    force Menu\Comics.menu

No change to package.json was detected. No package manager install will be executed.

This will create the ADMObject inside the ComicsComponents and a document inside the ComicsDocuments and also creates a menu point for the document in the module menu.

<Document>
    <Title localizable="true">Collections document</Title>
    <Object>Comics.Comics.ComicsDocuments.Collections</Object>
</Document>

When creating the document, in the CPP file, TBLegacy will create some default code, which will use the default primary key f_Code that you propably didn't use. So your next step should be to open {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\{ModuleName}Documents\D{DocumentName}.cpp and change the variable names that have been autogenerated.

3.3. Create a menu

In the last step, TBLegacy created a menu item for the newly created document. You can create menu items under {DevEnv_Path}\Standard\Applications\{ApplicationName}\{ModuleName}\Menu\{ModuleName}.menu.

<!-- Example → Comics menu -->
<?xml version="1.0" encoding="UTF-8"?>
<AppMenu>
    <Application name="Comics" image_namespace="Image.Comics.Comics.Images.LogoComicsSmall.png">
        <Title localizable="true">Comics description</Title>
        <Group name="Comics.Comics">
            <Title localizable="true">Comics Module</Title>
            <Menu name="ComicsTab">
                <Title localizable="true">Comics</Title>
                <Menu name="ComicsDocuments">
                    <Title localizable="true">Comics</Title>
                    <Document>
                        <Title localizable="true">Collections</Title>
                        <Object>Comics.Comics.ComicsDocuments.DCollection</Object>
                    </Document>
                    <Document>
                        <Title localizable="true">Boxes</Title>
                        <Object>Comics.Comics.ComicsDocuments.DBoxes</Object>
                    </Document>
                    <Batch>
                        <Title localizable="true">Copy Collections</Title>
                        <Object>Comics.Comics.ComicsDocuments.BDCollectionsCopy</Object>
                    </Batch>
                </Menu>
            </Menu>
            <Menu name="MangaTab">
                <Title localizable="true">Manga</Title>
                <Menu name="MangaDocuments">
                    <Title localizable="true">Manga</Title>
                    <Document>
                        <Title localizable="true">Story Arcs</Title>
                        <Object>Comics.Manga.MangaDocuments.DStoryArcs</Object>
                    </Document>
                </Menu>
            </Menu>
        </Group>
        <Group name="ERP.Preferences" image_namespace="Image.ERP.Core.Images.25x25\Settings.png" insert_after="all">
            <Title localizable="true">Preferences</Title>
            <Menu name="ComicsOptionsTab">
                <Title localizable="true">Comics</Title>
                <Menu name="ComicsOptions">
                    <Title localizable="true">Comics Options</Title>
                    <Document>
                        <Title localizable="true">Comics Parameters</Title>
                        <Object>Comics.Comics.ComicsDocuments.DComicsParameters</Object>
                    </Document>        
                </Menu>
            </Menu>
        </Group>
    </Application>
</AppMenu>

In this Example, we created a Mago4 main sidebar menu item, with <Group name="Comics.Comics">. You can also extend already existing Groups by referencing the corresponding namespace like with the second group <Group name="ERP.Preferences">.
Inside the Group node, the Menu nodes that host other Menu nodes will create the tab indexes. In this example, we would have a "Comics" and a "Manga" index tab, both given a name that would be displayed if there was no Title node inside.

<!-- Truncated Example → Comics menu -->
<Menu name="ComicsTab">
    <Title localizable="true">Comics</Title>
    [...]
</Menu>
<Menu name="MangaTab">
    <Title localizable="true">Manga</Title>
    [...]
</Menu>

The Menu nodes inside represent the "blocks" in which the documents, reports, etc. are displayed. You can differ between a <Document> and a <Batch> node for "BatchDocuments", special kinds of documents used for processing like copying documents or exports. Again the <Title> node can be used to give your document a display name. But more important is the <Object> node in which the namespace for the document is defined, like e.g. <Object>Comics.Comics.ComicsDocuments.DCollection</Object>.

3.4. Create a GUI for the document

Graphical User Interfaces are described in a JSON like language.
The files that describe a GUI for a specific document are found in ``{DevEnv_Path}\Standard\Applications{ApplicationName}{ModuleName}\ModuleObjects{DocumentName}\JsonForms`.

In that folder, you'll find .hjson files, in which you can define the elements that have to be referenced in your code and you'll have the .tbjson which describe the GUI:

/* Example → Main UI for Comics' Collection document */
{
  "id": "IDD_TD_COLLECTIONS_MAIN",
  "type": "Tile",
  "name": "MainData",
  "text": "Collections document",
  "child": true,
  "items": [
      {
      "id": "IDC_COLLECTIONS_CODE",
      "type": "Edit",
      "controlClass": "StringEdit",
      "controlCaption": "Code",
      "anchor": "COL1",
      "captionSize": 3,
      "controlSize": 2,
      "binding": {
        "datasource": "Collections.Code"
      },
      "tabStop": true,
      "width": 48,
      "height": 12,
      "autoHScroll": true,
      "rows": 1,
      "multiline": false
    },
    {
      "id": "IDC_COLLECTIONS_DESCRI",
      "type": "Edit",
      "controlClass": "StringEdit",
      "controlCaption": "Description",
      "anchor": "COL1",
      "captionSize": 3,
      "controlSize": 6,
      "binding": {
        "datasource": "Collections.Description"
      },
      "tabStop": true,
      "width": 154,
      "height": 36,
      "autoVScroll": true,
      "rows": 3,
      "multiline": true,
      "wantReturn": true
    }
    ],
  "hasStaticArea": true,
  "width": 654,
  "height": 73
}

Since there is no documentation about the tbjson format it is advised to take inspiration from a document that is already in the ERP module and copy the elements from there. [TODO?: Document tbjson more (keywords etc...)]

⚠️ **GitHub.com Fallback** ⚠️