Extending an archive to include extrafield?

Hi,

I have an extrafield on the contact-table (crm7.contact.x_discount_info) that I'd like to expose in CRM using an ArchiveExtenderExtender.

I've got the basics for a ContactExtender working, but not sure how I can tell it to include data from the extrafield.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SuperOffice.CRM.ArchiveLists;
using SuperOffice.CRM.Data;
using SuperOffice.Data;

namespace Testing
{
    [ArchiveExtenderExtender("ContactWithExtraFieldsExtension", typeof(ContactExtenderBase), int.MaxValue / 2)]
    public class ContactWithExtraFieldsExtender : TableExtenderBase<ContactTableInfo>
    {
        private ArchiveColumnInfo _colDiscountInfo
            = new ArchiveColumnInfo("x_discount_info", "Discount Info", "The discount set in Service",
                Constants.DisplayTypes.Int, true, true, "5c", Constants.RestrictionTypes.Int);

        protected override void InnerModifyQuery()
        {
        }

        protected override ContactTableInfo SetJoin()
        {
            var table = Parent.TableToExtend as ContactTableInfo;
            return table;
        }

        protected override void InnerPopulateRowFromReader(SoDataReader reader, ArchiveRow row)
        {
            row.AddOverlappingIntColumn("x_discount_info", 15); //Dummy data - how to get actual data?
        }
    }
}

Do I have to create a new TableInfo class for the Contact table? My hope is that there is some way to tell the ContactTableInfo that should also include the physical x_discount_info field.

RE: Extending an archive to include extrafield?

Hi Frode,

You should be able to add your field to the main query and then obtain the values from the query results in the reader, like so:

[ArchiveExtenderExtender("ContactWithExtraFieldsExtension", typeof(ContactExtenderBase), int.MaxValue / 2)]
public class ContactWithExtraFieldsExtender : TableExtenderBase<ContactTableInfo>
{
    private ArchiveColumnInfo _colDiscountInfo = new ArchiveColumnInfo(
        "x_discount_info", "Discount Info", "The discount set in Service",
        Constants.DisplayTypes.Int, true, true, "5c", Constants.RestrictionTypes.Int
        );

    FieldInfo xDiscount = null;

    public ContactWithExtraFieldsExtender()
    {
        ContactTableInfo cti = Parent.TableToExtend as ContactTableInfo;
        xDiscount = cti.FindField("x_discount_info");
    }

    protected override void InnerModifyQuery()
    {
        if (xDiscount != null)
        {
            RootQuery.Query.ReturnFields.Add(xDiscount);
        }
    }

    protected override void InnerPopulateRowFromReader(SoDataReader reader, ArchiveRow row)
    {
        base.InnerPopulateRowFromReader(reader, row);
        row.AddOverlappingIntColumn(_colDiscountInfo.Name, reader.GetInt32(xDiscount));
    }

    protected override ContactTableInfo SetJoin()
    {
        return Parent.TableToExtend as ContactTableInfo;
    }
}

Hope this helps!

Af: Tony Yates 8. dec 2017

RE: Extending an archive to include extrafield?

Thanks, that looks exactly like what I need! I'll test it when I get back infront of a computer.

My case has a second scenario as well, that I need to figure out how to do. I have an extratable, y_contact_data, that has three relevant fields. x_contact_id, which is a reference to the contact, x_user_id which is a user relationship, and x_has_agreement, which is a bool.

In this case, do I need to create a TableInfo class for my y_contact_data table? And if so, how do I add a Join in the C# code to get the data?

Af: Frode Lillerud 8. dec 2017

RE: Extending an archive to include extrafield?

Hi, I'm testing your code now. For some reason the Parent property used in the constructor is allways NULL. What could cause that?

Af: Frode Lillerud 8. dec 2017

RE: Extending an archive to include extrafield?

Hi Frode,

Yeah, sorry having that constructor in there wasn't the way to go... I was going off memory and didnt test it...

I have testing this though and should get you where you want:

[ArchiveExtenderExtender("ContactWithExtraFieldsExtension", 
    typeof(ContactExtenderBase), 
    int.MaxValue / 2)]
public class ContactWithExtraFieldsExtender : TableExtenderBase<ContactTableInfo>
{
    private ArchiveColumnInfo _colDiscountInfo = new ArchiveColumnInfo(
        "x_discount_info", "Discount Info", "The discount set in Service",
        Constants.DisplayTypes.Int, true, true, "5c", Constants.RestrictionTypes.Int
        );

    FieldInfo xDiscount = null;

    protected override void InnerModifyQuery()
    {
        ContactTableInfo cti = Parent.TableToExtend as ContactTableInfo;
        xDiscount = cti.FindField("x_discount_info");

        if (xDiscount != null)
        {
            RootQuery.Query.ReturnFields.Add(xDiscount);
        }
    }

    protected override void InnerPopulateRowFromReader(SoDataReader reader, ArchiveRow row)
    {
        base.InnerPopulateRowFromReader(reader, row);

        if(xDiscount != null)
        {
            row.AddOverlappingIntColumn(_colDiscountInfo.Name, reader.GetInt32(xDiscount));
        }
    }

    protected override ContactTableInfo SetJoin()
    {
        return Parent.TableToExtend as ContactTableInfo;
    }
}

In regards to the other issue, yes we have a way to do this. Give me a few minutes to type up an example and add that here afterwards.

Hope this helps (for now... )

Af: Tony Yates 8. dec 2017

RE: Extending an archive to include extrafield?

Here is an example how you would go about including an extra table into an extender. If you need to set up additional joins, depending on your relationships, etc, I'm confident you can work that out :-) If not, just give more details. :-)

(not thoroughly tested code)....

[ArchiveExtenderExtender("ContactWithExtraTableExtension", 
    typeof(ContactExtenderBase), int.MaxValue / 2)]
public class ContactWithExtraTableExtender : TableExtenderBase<TableInfo>
{
    private ArchiveColumnInfo _colUserId = new ArchiveColumnInfo(
        "x_user_id", "user identity", "user identity tooltip",
        Constants.DisplayTypes.Int, AllowOrderBy, Visible, 
        ColumnHelper.DefaultNumberWidth, Constants.RestrictionTypes.Int
        );

    private ArchiveColumnInfo _colHasAgreement = new ArchiveColumnInfo(
       "x_has_agreement", "Has Agrerement", "Has Agreement Tooltip",
       Constants.DisplayTypes.Bool, AllowOrderBy, Visible, 
       ColumnHelper.DefaultCheckboxWidth, Constants.RestrictionTypes.Bool
       );

    private ContactAgreementCache _contactAgreementTable = null;


    protected ContactWithExtraTableExtender()
    {
        _contactAgreementTable = ClassFactory.Create<ContactAgreementCache>();
        if (_contactAgreementTable.Y_ContactData == null)
            return;

        SetIconHint(Constants.IconHints.Contact);
    }
    protected override void InnerModifyQuery()
    {
        if (_ourTable != null)
        {
            MapSimpleReturnField(_contactAgreementTable.X_UserId, _colUserId);
            MapSimpleReturnField(_contactAgreementTable.X_HasAgreement, _colHasAgreement);
        }
    }

    protected override TableInfo SetJoin()
    {

        if (_contactAgreementTable.Y_ContactData == null)
            return null;

        var parentcontactInfo = Parent.TableToExtend as ContactTableInfo;

        RootQuery.Query.JoinRestriction.LeftOuterJoin(
            parentcontactInfo.ContactId.Equal(_contactAgreementTable.X_ContactId));

        RootQuery.Query.ReturnFields.Add(
            _contactAgreementTable.X_UserId, 
            _contactAgreementTable.X_HasAgreement);

        return _contactAgreementTable.Y_ContactData;
    }

    //No need to override unless you wanted custom tooltips, etc.
    //protected override void InnerPopulateRowFromReader(SoDataReader reader, ArchiveRow row)
    //{
    //}
}

/// <summary>
/// Cached data for the X-Table extenders
/// </summary>
[SoInject(InstanceContainers.Database)]
public class ContactAgreementCache
{
    public TableInfo Y_ContactData;

    public FieldInfo X_ContactId;
    public FieldInfo X_UserId;
    public FieldInfo X_HasAgreement;


    public ContactAgreementCache()
    {
        Y_ContactData = TablesInfo.GetTableInfo("y_contact_data");
        if (Y_ContactData != null)
        {
            X_ContactId     = Y_ContactData.FindField("x_contact_id");
            X_UserId        = Y_ContactData.FindField("x_user_id");
            X_HasAgreement  = Y_ContactData.FindField("x_has_agreement");
        }
    }
}

Hope this helps!

Af: Tony Yates 8. dec 2017

RE: Extending an archive to include extrafield?

Thanks, got the first example sort of working now. I can display the column in the result of a Company-selection, and it correctly displays the information from the extrafield. Very cool :)

However, if I use the Find-dialog to search for a company with a specific value in the extrafield, I always get the same two (wrong) results.

Do I need to write code for handling the search restrictions?

Af: Frode Lillerud 8. dec 2017

RE: Extending an archive to include extrafield?

Hi Frode,

No, you shouldn't need to handle any restrictions set in the UI.

You should be able to step into InnerModifyQuery and check the restrictions on RootQuery.Query.Restriction - and the whole query for that matter. 

Using your column name, you should be able to look at it using ExtractRestriction(...).

Best regards.

Af: Tony Yates 8. dec 2017

RE: Extending an archive to include extrafield?

Hi Tony, thanks for your help so far. 

When I inspect the RootQuery.Query.Restriction it is always null, even when I use standard fields in the Find-dialog.

If I override the AddLocalRestriction method, I see that the two restrictions are passed to it, as expected:

If I use the ExtractRestriction method inside InnerModify, I get the correct restriction back, even though RootQuery.Query.Restriction is null:

So my restriction seems to appear some places in the archiveprovider, but the searchresult is not correct.

Af: Frode Lillerud 9. dec 2017

RE: Extending an archive to include extrafield?

Hi again, 

I decided to expand on the example to include a lot of different field types. So, at this point I'm able to display several of the extrafields in the selection result:

There are some hickups; 

- Bools never get checked

- reader.GetBoolean crashes when databasefield contains NULL

- Not sure how I can display the company name in the Company Relation?

- DateTime only displays datepart, not time.

- Date uses wrong format

- User relations doesn't show anything

- Searching on any of the fields will always give me the same, wrong, result.

Here is the full code of the archive provider so far:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SuperOffice.CRM.ArchiveLists;
using SuperOffice.CRM.Data;
using SuperOffice.Data;
using SuperOffice.Data.SQL;

namespace GEASExperiments
{
    /*
     * An ArchiveExtender for exposing ExtraFields from Service in Sales.
     * This example assumes there are extrafields of various types on the crm7.contact table.
     * https://community.superoffice.com/en/developer/forum/rooms/topic/netserver-api-group/core-components/extending-an-archive-to-include-extrafield/
     * Frode Lillerud, 6.des 2017
     */

    [ArchiveExtenderExtender("ContactWithExtraFieldsExtension", typeof(ContactExtenderBase), int.MaxValue / 2)]
    public class ContactWithExtraFieldsExtender : TableExtenderBase<ContactTableInfo>
    {
        //Definition of the columns. NOTE - the variablename MUST start with _col to be detected by SuperOffice
        private ArchiveColumnInfo _colNumberColumn
            = new ArchiveColumnInfo("x_number", "Service Number", "Number field in Service",
                Constants.DisplayTypes.Int, true, true, ColumnHelper.DefaultNumberWidth, Constants.RestrictionTypes.Int);
        private FieldInfo xNumberField = null;

        private ArchiveColumnInfo _colDateColumn
            = new ArchiveColumnInfo("x_date", "Service Date", "Date field in Service",
                Constants.DisplayTypes.Date, true, true, ColumnHelper.DefaultDateWidth, Constants.RestrictionTypes.Date);
        private FieldInfo xDateField = null;

        private ArchiveColumnInfo _colBoolColumn
            = new ArchiveColumnInfo("x_bool", "Service Bool", "Bool field in Service",
                Constants.DisplayTypes.Bool, true, true, ColumnHelper.DefaultCheckboxWidth, Constants.RestrictionTypes.Bool);
        private FieldInfo xBoolField = null;

        private ArchiveColumnInfo _colTextShortColumn
            = new ArchiveColumnInfo("x_text_short", "Service Text Short", "Text (short) field in Service",
                Constants.DisplayTypes.String, true, true, ColumnHelper.DefaultStringWidth, Constants.RestrictionTypes.String);
        private FieldInfo xTextShortField = null;

        private ArchiveColumnInfo _colTextLongColumn
            = new ArchiveColumnInfo("x_text_long", "Service Text Long", "Text (long) field in Service",
                Constants.DisplayTypes.String, true, true, ColumnHelper.DefaultStringWidth, Constants.RestrictionTypes.String);
        private FieldInfo xTextLongField = null;

        private ArchiveColumnInfo _colCompanyRelationColumn
            = new ArchiveColumnInfo("x_company_relation", "Service Company Relation", "Company Relation field in Service",
                Constants.DisplayTypes.Int, true, true, "15c", Constants.RestrictionTypes.Entity);
        private FieldInfo xCompanyRelationField = null;

        private ArchiveColumnInfo _colFloatColumn
            = new ArchiveColumnInfo("x_float", "Service Float", "Float field in Service",
        Constants.DisplayTypes.Decimal, true, true, "15c", Constants.RestrictionTypes.Decimal);
        private FieldInfo xFloatField = null;

        private ArchiveColumnInfo _colDateTimeColumn
            = new ArchiveColumnInfo("x_datetime", "Service DateTime", "DateTime field in Service",
        Constants.DisplayTypes.Datetime, true, true, "15c", Constants.RestrictionTypes.Datetime);
        private FieldInfo xDateTimeField = null;

        private ArchiveColumnInfo _colTimeColumn
            = new ArchiveColumnInfo("x_time", "Service Time", "Time field in Service",
        Constants.DisplayTypes.Time, true, true, "15c", Constants.RestrictionTypes.Datetime);
        private FieldInfo xTimeField = null;

        private ArchiveColumnInfo _colUserRelationColumn
            = new ArchiveColumnInfo("x_user_relation", "Service User", "User Relation field in Service",
        Constants.DisplayTypes.Icon, true, true, "15c", Constants.RestrictionTypes.Associate);
        private FieldInfo xUserRelationField = null;

        public ContactWithExtraFieldsExtender()
        {
            //Add our field under the "Company" heading, and not the "Others" heading.
            SetIconHint(Constants.IconHints.Contact); 
        }

        protected override void InnerModifyQuery()
        {
            //If we haven't gotten the field definitions yet, then get them.
            if (xNumberField == null)
            {
                ContactTableInfo cti = Parent.TableToExtend as ContactTableInfo;
                xNumberField = cti.FindField("x_number");
                xDateField = cti.FindField("x_date");
                xBoolField = cti.FindField("x_bool");
                xTextShortField = cti.FindField("x_text_short");
                xTextLongField = cti.FindField("x_text_long");
                xCompanyRelationField = cti.FindField("x_company_relation");
                xFloatField = cti.FindField("x_float");
                xDateTimeField = cti.FindField("x_datetime");
                xTimeField = cti.FindField("x_time");
                xUserRelationField = cti.FindField("x_user_relation");
            }

            //Add the fields to the returned result.
            if (xNumberField != null)
                RootQuery.Query.ReturnFields.Add(xNumberField);
            if (xDateField != null)
                RootQuery.Query.ReturnFields.Add(xDateField);
            if (xBoolField != null)
                RootQuery.Query.ReturnFields.Add(xBoolField);
            if (xTextShortField != null)
                RootQuery.Query.ReturnFields.Add(xTextShortField);
            if (xTextLongField != null)
                RootQuery.Query.ReturnFields.Add(xTextLongField);
            if (xCompanyRelationField != null)
                RootQuery.Query.ReturnFields.Add(xCompanyRelationField);
            if (xFloatField != null)
                RootQuery.Query.ReturnFields.Add(xFloatField);
            if (xDateTimeField != null)
                RootQuery.Query.ReturnFields.Add(xDateTimeField);
            if (xTimeField != null)
                RootQuery.Query.ReturnFields.Add(xTimeField);
            if (xUserRelationField != null)
                RootQuery.Query.ReturnFields.Add(xUserRelationField);
        }

        protected override ContactTableInfo SetJoin()
        {
            return Parent.TableToExtend as ContactTableInfo;
        }

        protected override void InnerPopulateRowFromReader(SoDataReader reader, ArchiveRow row)
        {
            base.InnerPopulateRowFromReader(reader, row);

            //Fill the column in the returned row with the value from the field.
            if (xNumberField != null)
                row.AddOverlappingIntColumn("x_number", reader.GetInt32(xNumberField));
            if (xDateField != null)
                row.AddOverlappingDateTimeColumn("x_date", reader.GetDateTime(xDateField));
            if (xBoolField != null)
            {
                //bool b = false;
                //try
                //{
                //    b = reader.GetBoolean(xBoolField);
                //}
                //catch (Exception)
                //{
                //    //Data in field is NULL.
                //}
                row.AddOverlappingBoolColumn("x_bool", true); //Problem - reader.GetBoolean crashes when DB contains NULL
            }
            if (xTextShortField != null)
                row.AddOverlappingColumn("x_text_short", reader.GetString(xTextShortField));
            if (xTextLongField != null)
                row.AddOverlappingColumn("x_text_long", reader.GetString(xTextLongField));
            if (xCompanyRelationField != null)
                row.AddOverlappingIntColumn("x_company_relation", reader.GetInt32(xCompanyRelationField));
            if (xFloatField != null)
                row.AddOverlappingColumn("x_float", reader.GetDouble(xFloatField).ToString());
            if (xDateTimeField != null)
                row.AddOverlappingDateTimeColumn("x_datetime", reader.GetDateTime(xDateTimeField));
            if (xTimeField != null)
                row.AddOverlappingDateTimeColumn("x_time", reader.GetDateTime(xTimeField));
            if (xUserRelationField != null)
                row.AddOverlappingIntColumn("x_user_relation", reader.GetInt32(xUserRelationField));
        }
    }
}
Af: Frode Lillerud 9. dec 2017

RE: Extending an archive to include extrafield?

For future reference, Marek has a relevant example in his video from Expander World 2016: https://www.youtube.com/watch?v=XZWpevKS2DE&t=730s

Af: Frode Lillerud 20. dec 2017

RE: Extending an archive to include extrafield?

Anyone familiar with Archive Providers, which can see why the archive provider above doesn't return the correct result?

Af: Frode Lillerud 12. jan 2018

RE: Extending an archive to include extrafield?

Hi Frode,

When it comes to a DataReader, an understandably aged ADO.NET item, I don't expect many people to remember what typical coding patterns were required way back when. The way good code should be  written it to first check that the field is not null prior to trying to read it. That is the proper use of the API!

if (!reader.IsDBNull(xField))
{
    row.AddOverlappingColumn(...);
}

As for why your archive provider doesn't work as expected.... Do you have a valid SQL statement that can depicts what it is you are trying to accomplish? Something that works?

 

Af: Tony Yates 12. jan 2018

RE: Extending an archive to include extrafield?

Hi Tony, 
I see your point about checking for DBNULL.

Regarding searches - here is a minimalistic example where using the added column as a search criteria does not work.

using SuperOffice.CRM.ArchiveLists;
using SuperOffice.CRM.Data;
using SuperOffice.Data.SQL;

namespace GEASExperiments
{
    [ArchiveExtenderExtender("ContactExtraFields", typeof(ContactExtenderBase), int.MaxValue / 2)]
    public class ContactExtraFields : TableExtenderBase<ContactTableInfo>
    {
        private ArchiveColumnInfo _colTextShortColumn 
            = new ArchiveColumnInfo(
                "x_text_short", 
                "ExtraField TextShort", 
                "Text (short) field in Service", 
                Constants.DisplayTypes.String, 
                true, 
                true, 
                ColumnHelper.DefaultStringWidth, 
                Constants.RestrictionTypes.String
                );

        private FieldInfo xTextShortField = null;

        protected ContactExtraFields()
        {
            SetIconHint(Constants.IconHints.Contact);
        }

        protected override void InnerModifyQuery()
        {
            if (xTextShortField == null)
                xTextShortField = Parent.TableToExtend.FindField("x_text_short");

            if (xTextShortField != null)
            {
                MapSimpleReturnField(xTextShortField, _colTextShortColumn);

                var r1 = ExtractRestriction("x_text_short"); //This finds correct restriction 
                var r2 = RootQuery.Query.Restriction; //This is NULL
            }
        }
        
        protected override ContactTableInfo SetJoin()
        {
            return Parent.TableToExtend as ContactTableInfo;
        }
    }
}

This code just requires an extrafield called x_text_short on the crm7.contact table. Wherever I list companies and display the column, I can see the data from the x_text_short field, as expected.

However, if I try to use the field as a search criteria, the restriction doesn't seem to get picked up, and the result is not correct.

From the codeexample, in InnerModifyQuery, I can discover the restriction using ExtractRestriction(), but when I examine RootQuery.Query.Restriction it is NULL.

If I override the AddLocalRestriction and SetRestriction methods, and examine the values they receive, I see that they get my x_text_short restriction correctly.

So question is, what happens with the restriction?

Af: Frode Lillerud 12. jan 2018

RE: Extending an archive to include extrafield?

Anyone see what could be causing this?

Af: Frode Lillerud 22. jan 2018

RE: Extending an archive to include extrafield?

Hi Frode,

To understand what's going on here we need to look at the deep structure behind FindContact. The FindContact provider is implemented through an inhertiance hierarchy that adds various cool features, for instance that you can search for a combination of contact and person fields, some fields (email, phone, address) search on both levels but reassign the hit depending on what else you search for... and you can also search for Relations: Find contacts(a) that is related to another contact(b), where b.category = <mycategory>.

When you add an ExtenderExtender to ContactExtenderBase, you add it everywhere that ContactExtenderBase appears. In FindContact, there are three of them (not one as you might expect). There is the obvious, "root" ContactExtenderBase, which actually forms the base class of the FindContactExtender.

But there are two more, hanging off the Relations system, representing the outer endpoints of incoming and outgoing relation records. Like all other extenders, they generate outer joins unless they have criteria to add to the query.

Your ExtenderExtender gets added there, too. And through a mechanism that looks for restrictions and converts outer to inner joins, when you use your field as a restriction, those left outer joins to the relation table turn into inner joins. Unless the contacts your're trying to find happen to have relations, you will not see them.

The outer/inner join conversion is, strictly speaking, not really needed; if you put a clause in the WHERE part and it refers to a left-joined table, the join effectively becomes inner because NULL always gives a non-match. And the mechanism that discovers the restrictions seems to be missing a column prefix that is there to differentiate the contact extenders from each other, even though the actual sql generator isn't fooled. So there is room for improvement here, but not something to be lightly patched. I've put it on my "hmm" list of things to be carefully considered.

In the meantime, a possible, partial solution is to use the FindContactExtender as the target, instead of ContactExtenderBase. That works in Find, but not everywhere else. Another possible target is the CombinedContactExtenderBase, which is further up the hierarchy and gives you Find as well as Selection functionality.

Finally, why is the RootQuery.Query.Restriction empty? Because this is the final, SQL representation of all the restrictions. NetServer does not support parentheses (for grouping AND/OR) in its sql generator, but the ArchiveRestrictionInfo does (even if we don't show it in the GUI). So all restrictions are collected into slots in the Query.ExpansionHolder, where the sql that represents each single restriction is stored. When the whole thing is ready, a RestrictionParsingHelper performs a classic infix-to-binary-tree conversion so that the individual restrictions are correctly ordered and connected - and it's only after that, that the Query.Restriction is populated.

Af: Marek Vokáč 26. jan 2018

RE: Extending an archive to include extrafield?

Another point: How to get hold of field values in the InnerPopulateFromReader...

The tableInfo that you return from your SetJoin override, gets stored in a base class member called _ourTable. That particular tableInfo instance is the one that you need to refer to, when you work with fields.

You can do _ourTable.FindField("x_whatever") to get the fieldInfo, and save that in a member of your own, in InnerModifyQuery (like you already do, just refer to _ourTable as the source). That same fieldInfo is then used in InnerPopulateRow, reader.GetString(xTextShortField) will give you the value. You only need to do that if you have processing needs beyond what the base class does for you.

DBNULL checking is not needed for fetching data. NetServer doesn't really like the concept of null data (remember this was written before Nullable<> existed), and having to worry that what should have been an int might actually be null is a pain. I'm not saying that null-ness has no value (haha), only that in NetServer we early on decided to hide it.

So if you ask for a value from an SoDataReader, and it's null in the database, you will get the default value for the type instead of an exception like you would from a raw SqlDataReader. Int will give you 0, string gives you "", etc.

Af: Marek Vokáč 26. jan 2018

RE: Extending an archive to include extrafield?

Hello Marek,

same problem here.

I developed an ArchiveExtenderExtender for a Contact table our customer want to use for both searches and presenting fields.
It is based on a user table, in UDFields is no place more.

As long as it is using only for new fields in archive, it works correctly.
I was wondering why if I add to the class an "public override bool SetRestriction " I get no data more.

Now I know it.

I could make some Extender for searching, but I would like to avoid it: all the fields are then double.
Is there a workaround like setting all the joins LEFT instead of INNER in resulting query?

Thank you very much in advance!

 

 

Af: Andrey Stupak 25. aug 2020

RE: Extending an archive to include extrafield?

Now I implemented it with multiple provider extensions instead of ExtenderExtender:

FindContactSubProvider
SelectionAddMembersProvider
SelectionDynamicContactProvider
SelectionStaticContactProvider
FindAppointmentProvider
AppointmentSelectionDynamicProvider
AppointmentSelectionStaticProvider
FindDocumentProvider
DocumentSelectionDynamicProvider
DocumentSelectionStaticProvider
FindProjectProv­ider
ProjectSelectionDynamicProvider
ProjectSelectionStaticProvider
FindSaleProvider
SaleSelectionDynamicProvider
SaleSelectionStaticProvider

It works.

 

It would be great if the ExtenderExtender for Contact would have a full functionalty. It would significant simplify the developement of extenders.

Af: Andrey Stupak 26. aug 2020