SuperOffice CRM Professionals

Experienced experts in SuperOffice CRM - those primarily responsible for the setup, configuration, maintenance of SuperOffice in an organization, as well as those responsible for the implementation of applications or integrations, or provide consultancy services as to how an integration should be implemented using current industry standards.

Latest Forum Posts

Technical blog posts

Upcoming CRMScript / Developer improvements

Dear community, We have done some improvements to CRMScript upcoming release that we hope will be useful for you. The changes can be split into two main areas: improvements to CRMScript and improvements to the developer UI. CRMScript improvements For a few years now, we have had support for serializing/deserializing arrays and structs to JSON data. However, the support had some "gaps" in support for all basic types, and also the usage was a bit cumbersome having to go through XMLNode or JSONBuilder . To solve the latter, we have now introduced two new functions on arrays and struct : fromJsonString() and toJsonString() . The functions will allow you to serialize/deserialize in one swift code line: struct Person { String firstname; String lastname; Date dob; }; Person p; p.fromJsonString('{"firstname": "Jon", "lastname": "Doe", "dob": "1984-10-26"}'); printLine(p.toJsonString()); The observant amongst you will perhaps also notice that in the code above, we are deserializing and serializing a member of type Date. This was not supported before, but now we actually support members of all basic types: Integer, String, Bool, Float, Date, Time, DateTime, Byte, TimeSpan. For the types not supported natively by JSON, we will require/produce the following formats: Date: converted to/from string with format "YYYY-MM-DD" DateTime: converted to/from string with format "YYYY-MM-DDTHH:MI:SS" Time: converted to/from string with format "HH:MI:SS" Byte: converted to/from number. TimeSpan: converted to/from number (seconds) Support for these additional types also work for the existing fromXMLNode() and toJson() methods. Also, please note that fromJsonString() on arrays will work just like fromXMLNode() , which means it will append elements to the array and not clear it. Arrays have a separate .clear() method that can be called if you want to empty it first. Furthermore, we have introduced two new methods on arrays: buildString() and sort() .  buildString(String separator) is adding some functionality that has always existed on Vector ; the possibility to create a string (with a separator) from an array. The function will work as expected on basic types. (Complex types (e.g. an array of HTTP instances) will just result in a string with "[complex]"). The most useful example is probably to get a comma-separated array of integers: Integer[] arr; for (Integer i = 0; i In addition to basic types, we also support buildString() on arrays of structs. The default way to serialize a struct instance is to return its contents as a JSON string. However, there is now a way to override this by adding a member function with the following signature: String toString() : struct Person { String firstname; String lastname; String toString() { return this.firstname + " " + this.lastname; } }; Person[] persons; persons.fromJsonString('[{"firstname": "Mark","lastname": "Wahlberg"}]'); persons.fromJsonString('[{"firstname": "Uma","lastname": "Thurman"}]'); persons.fromJsonString('[{"firstname": "Tom","lastname": "Cruise"}]'); persons.fromJsonString('[{"firstname": "Michael J.","lastname": "Fox"}]'); printLine(persons.buildString(",")); The other new function for arrays is sort() , which will obviously sort the contents of the array. Only one-dimensional arrays are supported, and only arrays of basic types or structs. For basic types, this works as you would expect: Integer[] arr; for (Integer i = 0; i In order to be able to sort an array of structs, the struct must implement a method with the following signature: Bool compare(SameStruct s) . This will allow you to sort arrays of structs using whatever comparison you'd like: struct Person { String firstname; String lastname; String toString() { return this.firstname + " " + this.lastname; } Bool compare(Person p) { return this.toString()   Developer UI improvements This release contains lots of small and medium improvements to the developer experience in Service. Here are some of the highlights: Debugger/Tracing view When debugging in real time, or when viewing a saved script trace, we have now added a dropdown to the UI with all the source locations for the current debug/trace session. For large scripts that are using #includes, this allows you to quickly switch between the different sources. In debug mode, you can use this to e.g. set a breakpoint in another file. In tracing mode, clicking in the gutter (where the red breakpoints are shown) will now instead fast-forward the trace to that location. This can be very useful when viewing large script traces: instead of having to use the slider to try to find the frame where some particular code is executed, you can rather click next to the code and the slider will move to the correct position. Another small but welcome improvement to this view is that the width of the sidebar (containing info, variables, etc) now will be remembered in your browser and reused on subsequent views. We have also cleaned up the variable view a bit, and added a "copy value to clipboard" icon for each variable. Working with CRMScripts, Custom screens and Extra tables We have also done some changes to the UI when working with CRMScripts. First of all, the search functionality for scripts will now show the chosen result using the correct "View script" screen instead of a dump of the table contents. Also, when viewing a script, the "Run script" link now properly uses the includeId of the script if it is configured. Using the includeId-link instead of the id-link is recommended to make customizations more robust when migrating between instances of SuperOffice. When editing a CRMScript, we have now changed the output frame to use a monospaced font. This makes for instance SearchEngine.executeTextTable() look much better. One issue that has been quite annoying for some time is the complex process for creating a trace for the scripts related to a Custom screen. This has now been simplified by adding a new tab to the "View screen" screen, listing all the scripts related to this screen. Just click a script to create a trace for it. Finally, a small improvement in the view for listing extra tables. We have now added links for searching a table or creating a new record in a table from this view, so you don't have to navigate through Requests > Extra tables, and then find the same table you just edited. Together with some other bug fixes, we sincerely hope our latest version of SuperOffice will be a welcome improvement to your developer experience!

Michel Krohn-Dale
thumb_up5 mode_comment3

Quote Connector REST API proposal

Take a moment to read the Quote Connector REST API proposal  

Christian Mogensen
thumb_up1 mode_comment0

The future of NetServer

SuperOffice delivers multiple products. Customers see the Sales and Service applications, Customer’s customers see things like Chat, Forms, and the customer-facing side of Service; and both Customers and Partners see our programming interfaces. Those interfaces exist in several different ways. At the least-technical end we have configuration and preferences – these are “interfaces” that require no programming, but can still influence what the application does to a considerable degree. Then we have CRMScript and Configurable Screens, where a combination of programming and configuration is used to change the application. If we step into the more technical programming domain, we come to the Web Service API of NetServer, consisting of the endpoints that are strictly defined in our service model. Building NetServer web services based on service-oriented/service-agent architecture allows us to overlay (or expose) our API in various standardized ways, such as SOAP, REST, OData, and potentially other protocols based on industry trends.  These API’s are mostly accessed from programs, requiring a developer to write the code that uses them to perform some task. Up to this point, Online and Onsite installations offer the same API functionality; and they do so in a way that hides the implementation details (as well as many database details) from the caller. On a more technical level, we distribute SDK packages with the actual NetServer code, such that any public class XYZ is available to call. This gives access to lower-level code, including the ability to perform queries and updates on any table; access business logic. We also provide the ability to add logic with plugins that extend or override our functionality. It’s also possible to load a script file or DLL that our service API layer calls before and after any service API request is executed. Parallel to that, it’s possible to supply XML files that extend or override the logic in our PageBuilder user-interface engine; typically, in connection with backend plugins that provide server-side logic. These ways of accessing our code are only available Onsite , and it’s these that are the subject of the rest of this article. The world is changing – in fact it never stopped NetServer was designed quite some time ago, and I’m proud of the job the whole team did back then. While the world has progressed from .NET 1.0 up to today, the basics of NetServer have stayed the same – so much that we are almost completely compatible with code written 10 years ago. This has benefitted both us and the partners, providing a stable platform for development. But while we have held still, the world has moved. Some fashions have come and gone, some have persisted, and new ones have arrived. Now the distance between the world we designed for and the world of today has become so large that we must move with it. I am of course referring to .NET 6, but not only that, or at least not just the direct consequences. When Microsoft started the .NET Core project, one of their really fundamental decisions was to break compatibility with the platform . This means a valid .NET 4.8 program may not compile or run in .NET 6; and it may force a non-trivial rewrite. Examples of this include: When we first implemented NetServer, Windows Communication Foundation was brand new (SuperOffice was among the companies that sent a delegation to Redmond to beta test it). In .NET 6 it does not exist. NetServer was designed with “ambient variables” that keep track of identity, database, configuration and threading – as was the best practice at the time. .NET 6 is built around dependency injection and thus explicit management of these things. NetServer has a thread manager that is very conscious of when threads are created, run and destroyed; and integrates this tightly with the ambient variables. .NET 6 is totally oriented to async/await where actual threads are incidental and it’s in practice impossible to replicate the current NetServer approach. We are now at the point where Microsoft has effectively put .NET 4.8 on life support. The latest versions of the C# language are not supported (see link ). The tool chains are dying, and if you complain the answer is more and more often “hey, why are you still on 4.8?”. Finally, the demands and idioms of modern testing are not compatible with the ambient variables and “implicitness” of NetServer. It was a good pattern for its time, but that time is behind us. ‘nuff said – we have known for some time that we need to move, and we have started to do so. Compatibility – where and when The most important point I wish to make here is this: Our API endpoints – the Agents and their methods – will stay the same. If you are already integrating with us by calling them via REST, you’re fine. Read the rest if you want, or just go do something else. If you are calling us via WCF, please prepare to stop doing that and switch to REST. We will help, and we will keep WCF for a while yet, but just like .NET 4.8, WCF should be considered a dead end now. If you have NetServer in-process and are calling or extending our code directly, it’s going to be worse. It’s clear that we need to make some drastic refactoring in our code. Switching from ambient variables to explicit dependency injection is going to touch just about every class (even if much of the actual object management is done through factories, service providers and dependency resolvers at runtime). The next onsite release of NetServer will contain the first block of these changes. Startup of NetServer will no longer be implicit/lazy, but will instead use the standard startup and composition patterns from .NET 6. We have made startup methods for various environments (WinForms/desktop; IIS web; win service; …) but it doesn’t happen by itself – in fact that’s the point of having an explicit startup. It means that all integrations that run NetServer in process will have to add Startup code, suitable for their environment. But it doesn’t stop there. It doesn’t make sense for us to just make the least possible amount of changes, because that would just incur the costs without reaping the gains. There are many things that can be done once we’re on .NET 6 – think about async/await; operation cancellation; better state and identity handling. All of these will need changes to public classes: for instance, to enable cancellation all the way to the database, cancellation tokens will make an appearance and that will be reflected in various internal api’s. On top of that, there are functional refactoring's that we wish to make. There’s stuff in the userpreference table that should be elsewhere; the visiblefor table can be scrapped; user-defined fields and extra_fields should become the same thing, i.e. custom fields . There's 100.000 lines of generated code for Row field-change events that are never used. I could go on but the idea should be clear: None of these changes will break the Service API; all of them will break some class compatibility and many will break low-level database compatibility. A dark future? As always, SuperOffice strives to satisfy all parties – customers, partners and ourselves. We actually believe in our own slogan, that relations matter; and we’re not going to make life difficult for anyone on purpose. However, we both want and need to move with the rest of the industry; we can’t let our ecosystem become obsolete in the name of compatibility. That would ultimately hurt everyone. We therefore need to manage this transition as well as we possibly can. Our customers are migrating to the Cloud and in a sense that solves it, because the low-level integrations that get broken by our changes don’t exist there. We take the compatibility on the API level extremely seriously and do not plan to break it. As far as possible, we will try to give reasonable warning times when we see that a breaking change is coming; and we will do our best to have help and advice available on how to tackle it. Any such changes break our own code too, so we definitely eat our own dog food and should therefore be able to present solutions. We’d also like to involve partners to better understand what kind of changes hurt and how much; and if there are particular things we can do, avoid or retime to lessen the pain. All that said… we’ve spent more than a year on preparatory work. Migrating our code management to the cloud; rebuilding tool chains; cleaning out all sorts of old spiders and bones from our code; investigating different technologies and alternatives. Now we’re at the point where the actual transitional work has started, and it will accelerate throughout this year. While the transition may be a trifle rough, I very much look forward to when we are done. In tandem with the evolution of our Online platform, our code will be better positioned than ever to work in a Cloud environment, carrying developers, partners and customers from one era of computing to another. It's definitely not going to be boring.   Dr. Marek Vokáč Chief Engineer, SuperOffice R&D

Marek Vokáč
thumb_up11 mode_comment0

The story of the monorepo

On June 23rd, 2021, we announced after consolidating content across two community clubs, six stand-alone static websites, and several blog and forum posts. Behind the scenes, the old content was organized in five GitHub repositories: superoffice-docs , database , data-access, crmscript , and user-interface . Now, we have taken the consolidation one step further and combined these five repos into one : superoffice-doc s. This giant refactoring benefits authors and contributors and paves the road for future improvements. The published site is more or less the same (changes listed later in this post). Why As we gained experience from working with content tasks on the new site, we noticed some overhead : Conceptual information for company, contact, diary, document, project, quote, request, and sale was scattered throughout the five repos. Common settings (cspell), templates, and reusable text chunks were duplicated in each repo. We had to manage dependencies between repos. If you moved or renamed a file in repo A, you also had to make changes in the other repos to avoid broken links - which meant multiple branches and pull requests that had to be coordinated. We lost Git history when we moved content from one repo to another. The backlog was fragmented. We had to create an umbrella project and board to track and prioritize all issues in one location. The tools and build pipeline had to be aware of all repos and juggle Git submodules. It also added complexity for those who wanted to build the whole site locally. In addition, it was not always clear where something belonged. Contributors got confused and had to look in multiple repos before finding stuff. Solution Essentially, we did a sophisticated Git merge for each repo using a tool called git filter-repo followed by clean-up. Grouped how-tos. Grouped all downloadable files. Got rid of most duplicates. Moved all whats-new pages to superoffice-docs/release-notes/. Updated all links. Updated table-of-contents files (toc.yml). Renamed and moved physical folders to match the output URLs. Created a new build file, which turned out to be a third of the old one.   The four main graft points are: crmscript to superoffice-docs/docs/automation/crmscript/ database to superoffice-docs/docs/database/ data-access to superoffice-docs/docs/api/ user-interface to superoffice-docs/docs/ui/ You can read all about the details in the monorepo GitHub issue . Practical implications If you previously cloned or forked the crmscript, database, data-access, or user-interface repo, you need to switch to using the superoffice-docs repo .   The URLs to some API references have changed: CRMScript : automation/crmscript/reference/index.html Trigger scripts : automation/trigger/reference/index.html Webhooks : automation/webhook/reference/index.html bLogic screen elements : ui/blogic/reference/index.html Content about custom fields , udef, user-defined fields, extra fields, and extra tables is grouped under custom-objects/ . The NetServer API options for contact, company, diary, document, project, quote, sale, and request now have /howto/ instead of /api/ in the URL. The pages of the CRMScript "Working with..." section are now in the above-mentioned API option sections. In conclusion We hope you find this useful and interesting! Please let us know what you think by providing feedback on our GitHub discussion board . Go check it out! Disclaimer: You will need a github account, but the good news is that registration is super simple!

Bergfrid Skaara Dias
thumb_up6 mode_comment0

Spelling in code

Spelling mistakes always hit you when you're not expecting it. The harsh reality is that people will judge you on it. Nothing can make you or your company lose credibility more quickly. But I write code, not literary fiction... Yet you put your writing in front of other developers. Daily.     A typo in a code comment might trip up your co-worker reviewing your code. Or perhaps it goes undetected for years until they attempt to refactor or extend the code - after you left the company. Meanwhile, the comment is propagated to a public API reference where it can affect your company and trip up an unknown number of developers. Did someone say support case? OK. But let's say the typo was not in a comment, but actual code like a method name, property, or enum? Thanks to your IDE, IntelliSense will propagate your initial mistake like wildfire throughout the code-base. Awesome! Never, ever feed the Mogwai after midnight.     As with all bugs: the earlier you catch it, the cheaper it is to fix it. Who does not like bugs in production? The thrill of patches and hot-fixes.  But do you realize that once a misspelling is in the released API you have to live with it or break compatibility? Auch!   SuperOffice.CRM.ArchiveLists.AppointmentExtenderBase.ColumnRecurrenceRuleId SuperOffice.CRM.ArchiveLists.AppointmentExtenderBase._colRecurrencId The solution Slow down, read the actual words on the screen, and not what your autopilot concludes is there. Use a code spell checker. (Both for creating and reviewing.) Get a second pair of eyes. Own your oops'es and do the non-code-related PR change. Options VS Code: Code Spell Checker (cspell) Visual Studio: VS2017andLater | VS2022andLater (NHunSpell) Your milage might vary but I'm sure there is a spell checker match for you too.   Personally, I go the VS Code route with Code Spell Checker plus Grammarly. For SuperOffice Docs , we have a shared dictionary in .vscode/settings.json in each repo. You can check it out on GitHub .     (The covfefe level in my bloodstream is low. Please excuse any typos.)

Bergfrid Skaara Dias
thumb_up3 mode_comment0