• Solutions
    • FERC XBRL Reporting
    • FDTA Financial Reporting
    • SEC Compliance
    • Windows Clipboard Management
    • Legato Scripting
  • Products
    • GoFiler Suite
    • XBRLworks
    • SEC Exhibit Explorer
    • SEC Extractor
    • Clipboard Scout
    • Legato
  • Education
    • Training
    • SEC and EDGAR Compliance
    • Legato Developers
  • Blog
  • Support
  • Skip to blog entries
  • Skip to archive page
  • Skip to right sidebar

Friday, January 11. 2019

LDC #118: Bulk Filings Part 3

This week we will add a few more features to the bulk filing script. Now that Steve has filled in the code to create a bulk filing we can add the finishing touches to increase performance, check that the filing size is within acceptable limits, and improve the user interface. If you haven’t read the previous posts, here are the links to part one and two.


Let’s start with the user interface improvements. We received a request to add the contents of a folder to the list of filings. Each filing in the folder would be added to the bulk submission. In order to do this we need to add a button to the dialog as well as some code to the action function. Let’s look at the changes to the action function.



int bd_action(int id, int action) {

    string      tba[];
    string      path;
    string      ext;
    int         rc, ix, mx;

    switch (id) {
...
      case DC_ADDFOLDER:
        path = BrowseFolder("Select Projects");
        if (GetLastError() == ERROR_CANCEL) {
          break;
          }
        tba = EnumerateFiles(path, FOLDER_LOAD_NO_FOLDER_NAV | FOLDER_LOAD_RECURSE);
        mx = ArrayGetAxisDepth(tba);
        for (ix = 0; ix < mx; ix++) {
          ext = MakeLowerCase(GetExtension(tba[ix]));
          if ((ext == ".gfp") || (ext == ".eis")) {
            tba[ix] = AddPaths(path, tba[ix]);
            rc = ListBoxFindItem(DC_LIST, tba[ix]);
            if (IsError(rc)) {
              ListBoxAddItem(DC_LIST, tba[ix]);
              }
            }
          }
        bd_set_state();
        break;
...
      }
    return ERROR_NONE;
    }


The logic for this button is very similar to the add button. Instead of using BrowseOpenFiles we use BrowseFolder to allow the user to select the folder they wish to add. We then use the EnumerateFiles function to get an array of files within the folder and its children. Now that we have an array of files we can iterate over the list and add them to the list control. However, because we loaded all the files in the selected folder we need to filter the results to projects. We decided not to add XML files in the folders as too many EDGAR submissions have XML attachments. We do this by checking the extension of each file. We could change this to check the file type of each file but that requires opening each file to check the contents, which is a pretty slow operation. For now, we can rely on the user to select XML files individually.


We also improved the list control so when long names are added the contents of the list box scrolls. In order to accomplish this there is an additional line of code in the load function.



    ControlSendMessage(DC_LIST, LB_SETHORIZONTALEXTENT, 800, 0);


Since Legato doesn’t have a function to set the horizontal extent of a list box we can use the ControlSendMessage to talk directly to the list box. In this case we are telling the list box that we want to be able to scroll to 800 pixels.


This covers all of the user interface improvements, so let’s now look at how logic for the bulk filing creation. First, to improve performance we have changed the logic to write the bulk filing directly to disk as it is created. This means that Legato isn’t keeping a very large file in memory as it’s being created. Previously, the file was stored in a string pool which is memory efficient but requires the whole bulk file to be in memory. Since we are writing the file in sequential parts, writing the contents to disk as we go reduces the memory requirements.


Second, starting with GoFiler 4.25a, Legato now offers EDGAR Access Object functions. These functions allow for a project to be opened, edited and saved all in the background. This means the user doesn’t see the application open a project. This increases performance as creating and displaying the user interface for the project editor is quite expensive.


Here is the updated run function:



int run(int id, string mode) {

    int                 size;
    int                 bytes;
    int                 num_files;
    int                 ix;
    int                 rc;
    int                 limit;
    handle              hResult;
    handle              log;
    handle              hAttachment;
    handle              hEDAC;
    string              fname;
    string              enc_fname;
    string              outfile;
    string              params;
    string              test;
    string              file;
    string              attachment;

    if (mode != "preprocess") {
      return ERROR_NONE;
      }

    // Load Settings
    bulk = GetSetting("settings","bulk");
    files = ExplodeString(GetSetting("settings","files"),"|");

    // Run Dialog
    rc = DialogBox("BulkDialog", "bd_");
    if (IsError(rc)) {
      return rc;
      }

    // Set up
    log = LogCreate("Create Bulk Filing");
    hResult = CreateFile(bulk);
    if (IsError(hResult)) {
      MessageBox('x', "Could not save to file. %s", TranslateWindowsError(GetLastError()));
      return ERROR_FILE;
      }
    ProgressOpen("Creating Bulk Filing...", 0);

    // Start of File
    hAttachment = PoolCreate();
    WriteLine(hResult, OPEN);
    test = ReplaceInString(LIVE_FLAG, TEST_DIRECTIVE, live);
    WriteLine(hResult, test);

    // Write Attachments
    limit = -1;
    size = ArrayGetAxisDepth(files);
    WriteBlock(hResult, ATTACHMENTS_OPEN, GetStringLength(ATTACHMENTS_OPEN));
    for (ix = 0; ix < size; ix++) {
      ProgressUpdate(ix, size);
      ProgressSetStatus("File %d of %d", ix + 1, size);
      PoolReset(hAttachment);
      fname = files[ix];
      outfile = AddPaths(GetTempFileFolder(), GetFilename(fname));
      outfile = ClipFileExtension(outfile);
      outfile = outfile + ".xml";

      AddMessage(log, "Adding file %s to bulk filing", fname);
      hEDAC = EDACOpenFile(fname);
      if (IsError(hEDAC)) {
        rc = GetLastError();
        LogSetMessageType(LOG_ERROR);
        AddMessage(log, "  Cannot open filing, error 0x%08x", rc);
        LogSetMessageType(LOG_NONE);
        continue;
        }
      rc = EDACSaveFile(hEDAC, outfile);
      CloseHandle(hEDAC);
      if (IsError(rc)) {
        LogSetMessageType(LOG_ERROR);
        AddMessage(log, "  Cannot save filing, error 0x%08x", rc);
        LogSetMessageType(LOG_NONE);
        continue;
        }
      file = FileToString(outfile);
      rc = GetLastError();
      if (IsError(rc)) {
        LogSetMessageType(LOG_ERROR);
        AddMessage(log, "  Cannot add file to filing, error 0x%08x", rc);
        LogSetMessageType(LOG_NONE);
        continue;
        }

      // Build Attachment Code
      file = EncodeString(file);
      PoolAppend(hAttachment, ReplaceInString(ATTACHMENT_OPEN, NAME_DIRECTIVE, GetFilename(outfile)));
      PoolAppend(hAttachment, file);
      PoolAppend(hAttachment, ATTACHMENT_CLOSE);

      // Write to File
      attachment = PoolGetPool(hAttachment);
      WriteBlock(hResult, attachment, PoolGetPosition(hAttachment));
      DeleteFile(outfile);
      bytes = GetFilePosition(hResult);
      if (bytes >= SEC_FILE_LIMIT) {
        limit = ix;
        }
      num_files++;
      }

    // Finish File
    WriteLine(hResult, ATTACHMENTS_CLOSE);
    WriteLine(hResult, CLOSE);
    CloseHandle(hResult);
    ProgressClose();

    if (limit >= 0) {
      LogSetMessageType(LOG_WARNING);
      AddMessage(log, "  Bulk filing size (%d) exceeds EDGAR transmission (%d) limit.", GetFileSize(hResult), SEC_FILE_LIMIT);
      AddMessage(log, "  Remove files after entry %d (%s).", limit + 1, files[limit]);
      LogSetMessageType(LOG_NONE);
      }

    LogDisplay(log);
    MessageBox('i', "Added %d projects to bulk file %s.", num_files, bulk);
    return 0;
    }


As you can see there are many changes, but the core logic stayed the same. I will only go over the changed parts of the code. First we need a few more variables to track which file was the one that caused the bulk submission to exceed the SEC file size limit. Additionally, we no longer have a pool variable but rather a result handle for the file. Finally, we also now have a handle for the EDGAR Access Object.


We now start by using CreateFile to create a file to save the bulk data. If this fails, we can no longer continue so we stop processing. Otherwise we create a progress window using the ProgressOpen function. Now that the main set up is out of the way we can prepare the start of the file. Instead of writing to the pool variable we now use WriteLine to write directly to the file. We also reset the limit variable for later.


In the loop we start off by updating the progress window before we reset the attachment pool. This is part of the performance improvements. In the previous version the attachment pool was for all attachments. In this version of the script the attachment pool is only for the code of a single attachment. Because of this it should be reset each time the loop happens. The outfile variable has been changed slightly to instead use the temp area. We don’t want our script to overwrite other XML files that may be in the folder of our project. Most people use separate folders for each project but if someone does not this could have been an issue.


Now the real changes begin, instead of using RunMenuFunction we use the EDACOpenFile function to open each project. As stated above, this operation is significantly faster. We then use the EDACSaveFile function to have the application save an XML version of the project to the temp area. Other than the error reporting this portion of the code is the same as the last blog. The only big difference is we write to the file using WriteBlock.


Now that the attachment has been written to the file we can look to see if we exceeded the SEC submission limit. We get the position we are currently writing in the file. Since we are writing sequentially this position is equivalent to the size of the file. If this size is too large we mark which file it is. This is important since instead of just telling the user that the bulk filing is too large we can tell the user which file caused the submission to exceed the limit. That way they don’t have to guess how many files they need to remove to get the file size to be correct. They can instead remove that file and all the files beyond it in the list to create a smaller submission.


This concludes the loop. We wrap up by writing the close information for the bulk submission as well as close the progress window. If the file size was too large we add an additional warning to the log stating as such using the limit variable.


As Steve stated in the last blog the script was a good starting point. Now it has been improved further. Now all we need is the logic to submit the filing. If you are looking for a fun challenge, see if you can add the logic!


Here is the entire script file:



//
// Function Definitions and Globals
// --------------------------------

#define         TEST_DIRECTIVE          "%%%TEST%%%"
#define         NAME_DIRECTIVE          "%%%NAME%%%"
#define         OPEN                    "<?xml version=\"1.0\" ?>\r\n<bul:edgarBulkSubmission xmlns:bul=\"http://www.sec.gov/edgar/bulkfiling\">"
#define         LIVE_FLAG               "<bul:liveTestFlag>"+TEST_DIRECTIVE+"</bul:liveTestFlag>"
#define         ATTACHMENTS_OPEN        "<bul:attachments>"
#define         ATTACHMENTS_CLOSE       "\r\n</bul:attachments>"
#define         ATTACHMENT_OPEN         "\r\n<bul:attachment>\r\n<bul:submissionName>" + NAME_DIRECTIVE + "</bul:submissionName>\r\n<bul:contents>\r\n"
#define         ATTACHMENT_CLOSE        "</bul:contents>\r\n</bul:attachment>"
#define         CLOSE                   "</bul:edgarBulkSubmission>"
#define         SEC_FILE_LIMIT          209715200

int     bd_load                 ();
void    bd_set_state            ();
int     bd_action               (int id, int action);
int     bd_validate             ();

string  files[];
string  bulk;
string  live;
int     run(int id, string mode);
void    setup();

void main(){
    setup();
    }

void setup(){

    string              menu[];

    menu["Code"] = "CREATE_BULK_FILING";
    menu["MenuText"] = "Create Bulk Filing";
    menu["Description"] = "Compresses multiple GFP files into a single BULK XML File.";

    MenuAddFunction(menu);
    MenuSetHook("CREATE_BULK_FILING",GetScriptFilename(),"run");
    }

int run(int id, string mode) {

    int                 size;
    int                 bytes;
    int                 num_files;
    int                 ix;
    int                 rc;
    int                 limit;
    handle              hResult;
    handle              log;
    handle              hAttachment;
    handle              hEDAC;
    string              fname;
    string              enc_fname;
    string              outfile;
    string              params;
    string              test;
    string              file;
    string              attachment;

    if (mode != "preprocess") {
      return ERROR_NONE;
      }

    // Load Settings
    bulk = GetSetting("settings","bulk");
    files = ExplodeString(GetSetting("settings","files"),"|");

    // Run Dialog
    rc = DialogBox("BulkDialog", "bd_");
    if (IsError(rc)) {
      return rc;
      }

    // Set up
    log = LogCreate("Create Bulk Filing");
    hResult = CreateFile(bulk);
    if (IsError(hResult)) {
      MessageBox('x', "Could not save to file. %s", TranslateWindowsError(GetLastError()));
      return ERROR_FILE;
      }
    ProgressOpen("Creating Bulk Filing...", 0);

    // Start of File
    hAttachment = PoolCreate();
    WriteLine(hResult, OPEN);
    test = ReplaceInString(LIVE_FLAG, TEST_DIRECTIVE, live);
    WriteLine(hResult, test);

    // Write Attachments
    limit = -1;
    size = ArrayGetAxisDepth(files);
    WriteBlock(hResult, ATTACHMENTS_OPEN, GetStringLength(ATTACHMENTS_OPEN));
    for (ix = 0; ix < size; ix++) {
      ProgressUpdate(ix, size);
      ProgressSetStatus("File %d of %d", ix + 1, size);
      PoolReset(hAttachment);
      fname = files[ix];
      outfile = AddPaths(GetTempFileFolder(), GetFilename(fname));
      outfile = ClipFileExtension(outfile);
      outfile = outfile + ".xml";

      AddMessage(log, "Adding file %s to bulk filing", fname);
      hEDAC = EDACOpenFile(fname);
      if (IsError(hEDAC)) {
        rc = GetLastError();
        LogSetMessageType(LOG_ERROR);
        AddMessage(log, "  Cannot open filing, error 0x%08x", rc);
        LogSetMessageType(LOG_NONE);
        continue;
        }
      rc = EDACSaveFile(hEDAC, outfile);
      CloseHandle(hEDAC);
      if (IsError(rc)) {
        LogSetMessageType(LOG_ERROR);
        AddMessage(log, "  Cannot save filing, error 0x%08x", rc);
        LogSetMessageType(LOG_NONE);
        continue;
        }
      file = FileToString(outfile);
      rc = GetLastError();
      if (IsError(rc)) {
        LogSetMessageType(LOG_ERROR);
        AddMessage(log, "  Cannot add file to filing, error 0x%08x", rc);
        LogSetMessageType(LOG_NONE);
        continue;
        }

      // Build Attachment Code
      file = EncodeString(file);
      PoolAppend(hAttachment, ReplaceInString(ATTACHMENT_OPEN, NAME_DIRECTIVE, GetFilename(outfile)));
      PoolAppend(hAttachment, file);
      PoolAppend(hAttachment, ATTACHMENT_CLOSE);

      // Write to File
      attachment = PoolGetPool(hAttachment);
      WriteBlock(hResult, attachment, PoolGetPosition(hAttachment));
      DeleteFile(outfile);
      bytes = GetFilePosition(hResult);
      if (bytes >= SEC_FILE_LIMIT) {
        limit = ix;
        }
      num_files++;
      }

    // Finish File
    WriteLine(hResult, ATTACHMENTS_CLOSE);
    WriteLine(hResult, CLOSE);
    CloseHandle(hResult);
    ProgressClose();

    if (limit >= 0) {
      LogSetMessageType(LOG_WARNING);
      AddMessage(log, "  Bulk filing size (%d) exceeds EDGAR transmission (%d) limit.", GetFileSize(hResult), SEC_FILE_LIMIT);
      AddMessage(log, "  Remove files after entry %d (%s).", limit + 1, files[limit]);
      LogSetMessageType(LOG_NONE);
      }

    LogDisplay(log);
    MessageBox('i', "Added %d projects to bulk file %s.", num_files, bulk);
    return 0;
    }

//
// Dialog and Defines
// --------------------------------

#define DC_BULK         201
#define DC_LIST         301
#define DC_ADD          101
#define DC_ADDFOLDER    102
#define DC_REMOVE       103
#define DC_BROWSE       104
#define DC_LIVE         105
#define DC_CLEAR        106

#define ADD_FILTER      "All Projects|*.gfp;*.eis;*.xml;&GoFiler Projects|*.gfp&EDGAR Internet Submissions|*.eis&XML Submission Files|*.xml&All Files *.*|*.*"
#define SAVE_FILTER     "XML Submission Files|*.xml&All Files *.*|*.*"


int bd_load() {
    int         ix, mx;

    // Load List
    mx = ArrayGetAxisDepth(files);
    for (ix = 0; ix < mx; ix++) {
      ListBoxAddItem(DC_LIST, files[ix]);
      }

    ControlSendMessage(DC_LIST, LB_SETHORIZONTALEXTENT, 800, 0);

    EditSetText(DC_BULK, bulk);
    bd_set_state();
    return ERROR_NONE;
    }

void bd_set_state() {
    int         fx;
    int         ix;

    fx = ListBoxGetSelectIndex(DC_LIST);
    ix = ListBoxGetItemCount(DC_LIST);
    if (ix > 0) {
      ControlEnable(DC_CLEAR);
      }
    else {
      ControlDisable(DC_CLEAR);
      }
    if (fx >= 0) {
      ControlEnable(DC_REMOVE);
      }
    else {
      ControlDisable(DC_REMOVE);
      }
    }


int bd_action(int id, int action) {

    string      tba[];
    string      path;
    string      ext;
    int         rc, ix, mx;

    switch (id) {
      case DC_ADD:
        tba = BrowseOpenFiles("Select Projects", ADD_FILTER);
        if (GetLastError() == ERROR_CANCEL) {
          break;
          }
        mx = ArrayGetAxisDepth(tba);
        for (ix = 0; ix < mx; ix++) {
          rc = ListBoxFindItem(DC_LIST, tba[ix]);
          if (IsError(rc)) {
            ListBoxAddItem(DC_LIST, tba[ix]);
            }
          }
        bd_set_state();
        break;
      case DC_ADDFOLDER:
        path = BrowseFolder("Select Projects");
        if (GetLastError() == ERROR_CANCEL) {
          break;
          }
        tba = EnumerateFiles(path, FOLDER_LOAD_NO_FOLDER_NAV | FOLDER_LOAD_RECURSE);
        mx = ArrayGetAxisDepth(tba);
        for (ix = 0; ix < mx; ix++) {
          ext = MakeLowerCase(GetExtension(tba[ix]));
          if ((ext == ".gfp") || (ext == ".eis")) {
            tba[ix] = AddPaths(path, tba[ix]);
            rc = ListBoxFindItem(DC_LIST, tba[ix]);
            if (IsError(rc)) {
              ListBoxAddItem(DC_LIST, tba[ix]);
              }
            }
          }
        bd_set_state();
        break;
      case DC_REMOVE:
        rc = ListBoxGetSelectIndex(DC_LIST);
        if (rc >= 0) {
          ListBoxDeleteItem(DC_LIST, rc);
          while (rc >= ListBoxGetItemCount(DC_LIST)) {
            rc--;
            }
          if (rc >= 0) {
            ListBoxSetSelectIndex(DC_LIST, rc);
            }
          }
        bd_set_state();
        break;
      case DC_CLEAR:
        ListBoxReset(DC_LIST);
        bd_set_state();
        break;
      case DC_BROWSE:
        path = EditGetText(DC_BULK);
        path = BrowseSaveFile("Save Bulk Submission", SAVE_FILTER, path);
        if (GetLastError() == ERROR_CANCEL) {
          break;
          }
        EditSetText(DC_BULK, path);
        break;
      case DC_LIST:
        if (action == LBN_SELCHANGE) {
          bd_set_state();
          }
        break;
      }
    return ERROR_NONE;
    }

int bd_validate() {

    string      tba[];
    string      name;
    string      s1;
    int         ix;
    int         size;

    // Check List
    tba = ListBoxGetArray(DC_LIST);
    if (ArrayGetAxisDepth(tba) == 0) {
      MessageBox('x', "Submission must have at least one file.");
      return ERROR_SOFT | DC_LIST;
      }

    // Check Name
    name = EditGetText(DC_BULK);
    if (name == "") {
      MessageBox('x', "Bulk Submission File is a required field.");
      return ERROR_SOFT | DC_LIST;
      }
    if (MakeLowerCase(GetExtension(name)) != ".xml") {
      MessageBox('x', "Bulk Submission File must be an XML file");
      return ERROR_SOFT | DC_LIST;
      }
    if (CanAccessFile(name) == false){
      MessageBox('x', "Bulk Submission File must be a valid file location and not open by other applications.");
      return ERROR_SOFT | DC_LIST;
      }
    // Save Values
    bulk = name;
    files = tba;
    if (CheckboxGetState(DC_LIVE) == BST_CHECKED){
      live = "LIVE";
      }
    else{
      live = "TEST";
      }

    s1 = ImplodeArray(files, "|");
    PutSetting("settings", "bulk", bulk);
    PutSetting("settings", "files", s1);
    return ERROR_NONE;
    }

#beginresource

BulkDialog DIALOGEX 0, 0, 344, 190
EXSTYLE WS_EX_DLGMODALFRAME
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Bulk Filing"
FONT 8, "MS Shell Dlg", 400, 0
{
 CONTROL "Files", -1, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 4, 20, 8, 0
 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 26, 9, 314, 1, 0
 CONTROL "", DC_LIST, "listbox", LBS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP, 12, 16, 276, 120, 0
 CONTROL "&Add...", DC_ADD, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 292, 16, 45, 12, 0
 CONTROL "Add &Folder...", DC_ADDFOLDER, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 292, 31, 45, 12, 0
 CONTROL "&Remove", DC_REMOVE, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 292, 46, 45, 12, 0
 CONTROL "&Clear", DC_CLEAR, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 292, 61, 45, 12, 0
 CONTROL "&LIVE", DC_LIVE, "button", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 296, 76, 45, 12, 0
 CONTROL "Submission", -1, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 137, 42, 8, 0
 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 48, 142, 292, 1, 0
 CONTROL "&Bulk Submission File:", -1, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 12, 150, 72, 8, 0
 CONTROL "", DC_BULK, "edit", ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 88, 148, 200, 12
 CONTROL "Browse...", DC_BROWSE, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 292, 148, 45, 12, 0
 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 6, 166, 335, 1, 0
 CONTROL "&Create", IDOK, "BUTTON", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 232, 172, 50, 14
 CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 287, 172, 50, 14
}

#endresource


 


David Theis has been developing software for Windows operating systems for over fifteen years. He has a Bachelor of Sciences in Computer Science from the Rochester Institute of Technology and co-founded Novaworks in 2006. He is the Vice President of Development and is one of the primary developers of GoFiler, a financial reporting software package designed to create and file EDGAR XML, HTML, and XBRL documents to the U.S. Securities and Exchange Commission.

Additional Resources

Novaworks’ Legato Resources

Legato Script Developers LinkedIn Group

Primer: An Introduction to Legato 



Posted by
David Theis
in Development at 16:09
Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)
No comments
The author does not allow comments to this entry

Quicksearch

Categories

  • XML Accounting
  • XML AICPA News
  • XML FASB News
  • XML GASB News
  • XML IASB News
  • XML Development
  • XML Events
  • XML FERC
  • XML eForms News
  • XML FERC Filing Help
  • XML Filing Technology
  • XML Information Technology
  • XML Investor Education
  • XML MSRB
  • XML EMMA News
  • XML FDTA
  • XML MSRB Filing Help
  • XML Novaworks News
  • XML GoFiler Online Updates
  • XML GoFiler Updates
  • XML XBRLworks Updates
  • XML SEC
  • XML Corporation Finance
  • XML DERA
  • XML EDGAR News
  • XML Investment Management
  • XML SEC Filing Help
  • XML XBRL
  • XML Data Quality Committee
  • XML GRIP Taxonomy
  • XML IFRS Taxonomy
  • XML US GAAP Taxonomy

Calendar

Back May '25 Forward
Mo Tu We Th Fr Sa Su
Sunday, May 18. 2025
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Feeds

  • XML
Sign Up Now
Get SEC news articles and blog posts delivered monthly to your inbox!
Based on the s9y Bulletproof template framework

Compliance

  • FERC
  • EDGAR
  • EMMA

Software

  • GoFiler Suite
  • SEC Exhibit Explorer
  • SEC Extractor
  • XBRLworks
  • Legato Scripting

Company

  • About Novaworks
  • News
  • Site Map
  • Support

Follow Us:

  • LinkedIn
  • YouTube
  • RSS
  • Newsletter
  • © 2024 Novaworks, LLC
  • Privacy
  • Terms of Use
  • Trademarks and Patents
  • Contact Us