-
Notifications
You must be signed in to change notification settings - Fork 365
External Storage - Document Attachments #4495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
JesperSchulz
merged 79 commits into
microsoft:main
from
StefanSosic:dev/sso/externalStorageApp
Feb 12, 2026
Merged
Changes from 43 commits
Commits
Show all changes
79 commits
Select commit
Hold shift + click to select a range
c8e19e2
Add integration events for file scenario management and enhance UI ac…
StefanSosic 33071a9
Merge branch 'main' of https://github.com/StefanSosic/BCApps into dev…
StefanSosic b81f6eb
External Storage - Document Attachments
StefanSosic 4adc2b0
Interface + Improvements
StefanSosic d04ef8b
Fixes
StefanSosic 4978d52
Trailing space
StefanSosic 6357709
Internal
StefanSosic e5dbc09
Internal
StefanSosic 6ecd65f
app json fixes
StefanSosic c454a9a
DataClassification
StefanSosic b485579
Namespaces
StefanSosic a28fe49
InherentPermissions + InherentEntitlements
StefanSosic 26e1c3a
Update tooltip for sync direction field in DA External Storage Sync r…
StefanSosic 68ef801
Deafult Scenario
StefanSosic 8832186
Empty Line
StefanSosic ecd581a
Permission Refactor & Message
StefanSosic 4b57268
Add label for date formula in DA External Storage Sync report
StefanSosic 7eb7ebf
FIxes from PR comments
StefanSosic bf2f5b6
Fixes and Features
StefanSosic 509618d
Add disable check
StefanSosic 244043f
Remove prefix
StefanSosic 0db7610
Adjustments to deletion
StefanSosic ad8638e
Remove logger
StefanSosic e87efe6
Merge branch 'main' of https://github.com/StefanSosic/BCApps into dev…
StefanSosic f358ddb
Update readme.md
StefanSosic b6c89f6
Improve warning message
JesperSchulz 4f4c207
Fallback for table name if 3rd party App Uninstalled
StefanSosic 372d316
Merge branch 'dev/sso/externalStorageApp' of https://github.com/Stefa…
StefanSosic f12957b
Add migration report
StefanSosic 14164b2
File Scenario fix breaking change
StefanSosic 3e86854
Merge branch 'main' of https://github.com/StefanSosic/BCApps into dev…
StefanSosic 53648cf
Remove
StefanSosic 852773a
Fix
StefanSosic 3e45ce4
Remove upload
StefanSosic 3e9f8a5
Fix Copy
StefanSosic fca4ef1
Edge case
StefanSosic 4edda71
One Drive
StefanSosic 90f2a8a
Polishing
StefanSosic 36318ea
Merge branch 'main' of https://github.com/microsoft/BCApps into dev/s…
JesperSchulz 3c06e87
Fixes & Polishing
StefanSosic c3fe884
Merge branch 'main' of https://github.com/microsoft/BCApps into dev/s…
JesperSchulz 82a537f
Add setup page invocation automatically on insert scenario
StefanSosic 2687d6c
Improvements
StefanSosic e625564
Merge branch 'dev/sso/externalStorageApp' of https://github.com/stefa…
JesperSchulz 913abe5
Report optimize
StefanSosic cdd9fa2
Merge branch 'dev/sso/externalStorageApp' of https://github.com/stefa…
JesperSchulz fb1d1dd
Drill down
StefanSosic b34c455
Init true
StefanSosic c55ef6f
Merge branch 'dev/sso/externalStorageApp' of https://github.com/stefa…
JesperSchulz 94e326b
Fix
StefanSosic 07ed610
Merge branch 'dev/sso/externalStorageApp' of https://github.com/stefa…
JesperSchulz 1fd2540
Merge branch 'main' of https://github.com/StefanSosic/BCApps into dev…
StefanSosic b7ffcc4
Add tests for External Storage - Document Attachments functionality
StefanSosic 585f7f8
New Guid
StefanSosic 5eeff8d
Merge branch 'dev/sso/externalStorageApp' of https://github.com/stefa…
JesperSchulz bd39320
Merge branch 'main' of https://github.com/microsoft/BCApps into dev/s…
JesperSchulz 711568d
Merge branch 'main' of https://github.com/microsoft/BCApps into dev/s…
JesperSchulz 8c2d4f0
Updates
StefanSosic 8d65717
Update to 32
StefanSosic acd40e0
Merge branch 'dev/sso/externalStorageApp' of https://github.com/stefa…
JesperSchulz d9af01e
Merge branch 'main' of https://github.com/microsoft/BCApps into dev/s…
JesperSchulz ebffc34
Merge branch 'main' of https://github.com/microsoft/BCApps into dev/s…
JesperSchulz 2168b02
Merge branch 'dev/sso/externalStorageApp' of https://github.com/stefa…
JesperSchulz a0ce75c
Merge branch 'main' of https://github.com/StefanSosic/BCApps into dev…
StefanSosic 791c1f4
Merge branch 'dev/sso/externalStorageApp' of https://github.com/Stefa…
StefanSosic ebc2b5d
Merge branch 'dev/sso/externalStorageApp' of https://github.com/stefa…
JesperSchulz 11c33b1
Add telemetry tags.
JesperSchulz 32202ff
Update src/Apps/W1/External Storage - Document Attachments/app/src/Se…
StefanSosic ae20d03
Update src/System Application/App/External File Storage/src/Scenario/…
StefanSosic 929df45
Update src/System Application/App/External File Storage/src/Scenario/…
StefanSosic bf03c5c
Fixes to PR Comments
StefanSosic 87a03cc
Merge branch 'dev/sso/externalStorageApp' of https://github.com/Stefa…
StefanSosic 0ca6800
Update src/System Application/App/External File Storage/src/Scenario/…
StefanSosic af4a2cb
Updates
StefanSosic 717fe2c
All ApplicationArea
StefanSosic 5f375f8
Permissions
StefanSosic f477e66
Entitlements
StefanSosic 220dc0d
Merge branch 'main' of https://github.com/microsoft/BCApps into dev/s…
JesperSchulz 4f111fe
Merge branch 'dev/sso/externalStorageApp' of https://github.com/stefa…
JesperSchulz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file added
BIN
+5.32 KB
src/Apps/W1/External Storage - Document Attachments/app/ExtensionLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
159 changes: 159 additions & 0 deletions
159
src/Apps/W1/External Storage - Document Attachments/app/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| # Document Attachments External Storage for Microsoft Dynamics 365 Business Central | ||
|
|
||
| ## Overview | ||
|
|
||
| The External Storage extension provides seamless integration between Microsoft Dynamics 365 Business Central and external storage systems such as Azure Blob Storage, SharePoint, and File Shares. This extension automatically manages document attachments by storing them in external storage systems while maintaining full functionality within Business Central. | ||
|
|
||
| ## Key Features | ||
|
|
||
| ### **Multi-Tenant, Multi-Environment, and Multi-Company Support** | ||
| - **Environment Hash**: Unique hash based on Tenant ID + Environment Name + Company System ID | ||
| - **Organized Folder Structure**: Files are stored in hierarchical folders: `RootFolder/EnvironmentHash/TableName/FileName` | ||
| - **Cross-Environment Compatibility**: Files from different tenants, environments, or companies are properly isolated | ||
| - **Migration Support**: Built-in migration tool to move files between company folders when needed | ||
| - **Environment Hash Display**: View current environment hash for reference and troubleshooting | ||
|
|
||
| ### **Flexible Deletion Policies** | ||
| - **Delete from External Storage**: Optionally delete files from external storage when attachments are removed from BC | ||
| - **Automatic Cleanup**: Scheduled job queue can automatically delete expired files based on retention policy | ||
|
|
||
| ### **Customizable Root Folder** | ||
| - Configure a custom root folder path for all attachments | ||
| - Interactive folder browser for easy selection | ||
| - Automatic folder creation and hierarchy management | ||
|
|
||
| ### **Bulk Operations** | ||
| - Synchronize multiple files between internal and external storage | ||
| - Bulk upload to external storage | ||
| - Bulk download from external storage | ||
| - Progress tracking with detailed reporting | ||
|
|
||
| ## Installation & Setup | ||
|
|
||
| ### Prerequisites | ||
| - Microsoft Dynamics 365 Business Central version 28.0 or later | ||
| - File Account module configured with external storage connector | ||
| - Appropriate permissions for file operations | ||
|
|
||
| ### Installation Steps | ||
|
|
||
| 1. **Configure File Account** | ||
| - Open **File Accounts** page | ||
| - Create a new File Account with your preferred connector: | ||
| - Azure Blob Storage | ||
| - SharePoint | ||
| - File Share | ||
| - Assign the account to **External Storage** scenario | ||
|
|
||
| 2. **Configure External Storage** | ||
| - Open **File Accounts** page | ||
| - Select assigned **External Storage** scenario | ||
| - Open **Additional Scenario Setup** | ||
| - Configure settings: | ||
| - **Enabled**: Enable the External Storage feature | ||
| - **Root Folder**: Select the root folder path for attachments (use AssistEdit to browse) | ||
| - **Delete from External Storage**: Enable to delete external files when attachments are removed from BC | ||
|
|
||
| ### Configuration Options | ||
|
|
||
| #### General Settings | ||
| - **Enabled**: Master switch to activate/deactivate the External Storage feature | ||
| - **Root Folder**: Base folder path in external storage where all attachments will be organized | ||
| - Files are stored in: `RootFolder/EnvironmentHash/TableName/FileName` | ||
| - Use AssistEdit button to browse and select folder interactively | ||
|
|
||
| #### Upload and Delete Policy | ||
| - **Delete from External Storage**: When enabled, files are deleted from external storage when the attachment is removed from Business Central | ||
|
|
||
|
|
||
| ## Usage | ||
|
|
||
| ### Multi-Company and Multi-Environment Support | ||
|
|
||
| #### Environment Hash | ||
| Every file uploaded to external storage includes an environment hash that uniquely identifies: | ||
| - **Tenant ID**: Your Business Central tenant | ||
| - **Environment Name**: Current environment (e.g., Production, Sandbox) | ||
| - **Company System ID**: Unique identifier for the company | ||
|
|
||
| This ensures files from different tenants, environments, or companies are properly isolated in external storage. | ||
|
|
||
| #### Folder Structure | ||
| Files are organized hierarchically: | ||
| ``` | ||
| RootFolder/ | ||
| ├── [EnvironmentHash-1]/ | ||
| │ ├── Sales_Header/ | ||
| │ │ └── invoice-{guid}.pdf | ||
| │ └── Purchase_Header/ | ||
| │ └── order-{guid}.pdf | ||
| └── [EnvironmentHash-2]/ | ||
| └── Sales_Header/ | ||
| └── quote-{guid}.pdf | ||
| ``` | ||
|
|
||
| #### File Migration | ||
| When moving data between environments or companies: | ||
| 1. Open **External Storage Setup** page | ||
| 2. Click **Migrate Files** action | ||
| 3. System automatically: | ||
| - Identifies files from previous environment/company | ||
| - Copies files to current environment/company folder structure | ||
| - Updates file paths and environment hash | ||
| - Maintains all file metadata and associations | ||
|
|
||
| #### Environment Hash Display | ||
| - Click **Show Current Environment Hash** to view your current hash | ||
| - Use this hash to identify your files in external storage | ||
| - Helpful for troubleshooting and cross-environment scenarios | ||
|
|
||
| ### Manual Operations | ||
|
|
||
| #### Setup Page Actions | ||
| From **External Storage Setup** page: | ||
| - **Storage Sync**: Run synchronization manually to upload/download files | ||
| - **Migrate Files**: Migrate all files from previous environment/company to current folder structure | ||
| - **Show Current Environment Hash**: Display the current environment hash for reference | ||
| - **Document Attachments**: Open the list of all document attachments with external storage information | ||
|
|
||
| #### Individual File Operations | ||
| From **Document Attachment - External** page: | ||
| - **Upload to External Storage**: Upload selected file manually | ||
| - **Download from External Storage**: Download file for viewing | ||
| - **Download to Internal Storage**: Restore file to internal storage | ||
| - **Delete from External Storage**: Remove file from external storage | ||
| - **Delete from Internal Storage**: Remove file from internal storage only | ||
|
|
||
| #### Bulk Operations | ||
| From **External Storage Synchronize** report: | ||
| - **To External Storage**: Upload multiple files to external storage | ||
| - **From External Storage**: Download multiple files from external storage | ||
| - **Delete Expired Files**: Clean up files based on retention policy | ||
|
|
||
| ### File Access and Compatibility | ||
| - Files uploaded to external storage remain fully accessible through standard Business Central functionality | ||
| - Document preview, download, and management work seamlessly | ||
| - Files deleted internally are automatically retrieved from external storage when accessed | ||
| - No change to end-user experience | ||
| - Cross-environment and cross-company access is handled automatically | ||
|
|
||
| ## Important Notes | ||
|
|
||
| ### Data Safety | ||
| - **This extension is provided as-is** | ||
| - Always maintain proper backups of your external storage | ||
| - Test thoroughly in a sandbox environment before production use | ||
| - Verify file accessibility after migration | ||
|
|
||
| ### Environment Changes | ||
| - When moving between environments, use the **Migrate Files** action | ||
| - Environment hash changes with tenant, environment, or company changes | ||
| - Files from previous environments are not automatically deleted | ||
| - Manual cleanup of old environment folders may be required | ||
|
|
||
| ### Feature Disable Protection | ||
| - Cannot disable External Storage setup if files are uploaded | ||
| - Must delete all uploaded files before disabling the feature | ||
| - Cannot unassign External Storage scenario if files exist in external storage | ||
|
|
||
| **© 2025 Microsoft Corporation. All rights reserved.** |
35 changes: 35 additions & 0 deletions
35
src/Apps/W1/External Storage - Document Attachments/app/app.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| { | ||
| "id": "5f2e93a0-6083-4718-b05a-7ac89be5644d", | ||
| "name": "External Storage - Document Attachments", | ||
| "publisher": "Microsoft", | ||
| "version": "28.0.0.0", | ||
| "brief": "External Storage processor for Business Central document attachments", | ||
| "description": "Processes document attachments from Business Central and stores them in configured External Storage connectors (Azure Blob, File Share, SharePoint)", | ||
| "privacyStatement": "https://go.microsoft.com/fwlink/?linkid=724009", | ||
| "EULA": "https://go.microsoft.com/fwlink/?linkid=2009120", | ||
| "help": "https://go.microsoft.com/fwlink/?linkid=2134520", | ||
| "url": "https://go.microsoft.com/fwlink/?linkid=724011", | ||
| "logo": "ExtensionLogo.png", | ||
| "application": "28.0.0.0", | ||
| "platform": "28.0.0.0", | ||
| "internalsVisibleTo": [ | ||
| ], | ||
| "dependencies": [], | ||
| "screenshots": [], | ||
| "idRanges": [ | ||
| { | ||
| "from": 8750, | ||
| "to": 8770 | ||
| } | ||
| ], | ||
| "resourceExposurePolicy": { | ||
| "allowDebugging": true, | ||
| "allowDownloadingSource": true, | ||
| "includeSourceInSymbolFile": true | ||
| }, | ||
| "features": [ | ||
| "TranslationFile" | ||
| ], | ||
| "contextSensitiveHelpUrl": "https://go.microsoft.com/fwlink/?linkid=2134520", | ||
| "target": "Cloud" | ||
| } |
124 changes: 124 additions & 0 deletions
124
...Storage - Document Attachments/app/src/AutomaticSync/DAExternalStorageMigration.Report.al
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| // ------------------------------------------------------------------------------------------------ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. See License.txt in the project root for license information. | ||
| // ------------------------------------------------------------------------------------------------ | ||
|
|
||
| namespace Microsoft.ExternalStorage.DocumentAttachments; | ||
|
|
||
| using Microsoft.Foundation.Attachment; | ||
|
|
||
| /// <summary> | ||
| /// Report for migrating document attachments from previous environment/company folder to current environment/company folder. | ||
| /// Can be scheduled via job queue for background processing. | ||
| /// </summary> | ||
| report 8753 "DA External Storage Migration" | ||
| { | ||
| Caption = 'External Storage Migration'; | ||
| ProcessingOnly = true; | ||
| UseRequestPage = true; | ||
| Extensible = false; | ||
| ApplicationArea = Basic, Suite; | ||
| UsageCategory = None; | ||
| Permissions = tabledata "DA External Storage Setup" = r, | ||
| tabledata "Document Attachment" = rimd; | ||
|
|
||
| dataset | ||
| { | ||
| dataitem(DocumentAttachment; "Document Attachment") | ||
| { | ||
| trigger OnPreDataItem() | ||
| begin | ||
| // Filter for files that are uploaded externally and need migration | ||
| SetRange("Stored Externally", true); | ||
| SetFilter("External File Path", '<>%1', ''); | ||
|
|
||
| TotalCount := Count(); | ||
|
|
||
| if TotalCount = 0 then begin | ||
| if GuiAllowed() then | ||
| Message(NoFilesToMigrateLbl); | ||
| CurrReport.Break(); | ||
| end; | ||
|
|
||
| ProcessedCount := 0; | ||
| MigratedCount := 0; | ||
| FailedCount := 0; | ||
|
|
||
| if GuiAllowed() then | ||
| Dialog.Open(ProcessingMsg, TotalCount); | ||
| end; | ||
|
|
||
| trigger OnAfterGetRecord() | ||
| begin | ||
| ProcessedCount += 1; | ||
|
|
||
| if GuiAllowed() then | ||
| Dialog.Update(1, ProcessedCount); | ||
|
|
||
| // Check if file needs migration | ||
| if ExternalStorageImpl.IsFileFromAnotherEnvironmentOrCompany(DocumentAttachment) then | ||
| if ExternalStorageImpl.MigrateFileToCurrentEnvironment(DocumentAttachment) then | ||
| MigratedCount += 1 | ||
| else | ||
| FailedCount += 1; | ||
|
|
||
| Commit(); // Commit after each record to avoid lost in communication error with external storage service | ||
| end; | ||
|
|
||
| trigger OnPostDataItem() | ||
| begin | ||
| if MigratedCount > 0 then | ||
| LogMigrationTelemetry(); | ||
|
|
||
| if GuiAllowed() then begin | ||
| if TotalCount <> 0 then | ||
| Dialog.Close(); | ||
|
|
||
| if MigratedCount > 0 then | ||
| Message(MigrationCompletedMsg, MigratedCount, FailedCount) | ||
| else | ||
| Message(NoFilesToMigrateLbl); | ||
| end; | ||
| end; | ||
| } | ||
| } | ||
|
|
||
| requestpage | ||
| { | ||
| SaveValues = true; | ||
|
|
||
| layout | ||
| { | ||
| area(Content) | ||
| { | ||
| group(General) | ||
| { | ||
| Caption = 'General'; | ||
| label(InfoLabel) | ||
| { | ||
| ApplicationArea = Basic, Suite; | ||
| Caption = 'This report will migrate all document attachments from a previous environment or company folder to the current environment/company folder in external storage.'; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| var | ||
| ExternalStorageImpl: Codeunit "DA External Storage Impl."; | ||
| Dialog: Dialog; | ||
| FailedCount: Integer; | ||
| MigratedCount: Integer; | ||
| ProcessedCount: Integer; | ||
| TotalCount: Integer; | ||
| MigrationCompletedMsg: Label '%1 file(s) have been successfully migrated to the current company folder. %2 failed.', Comment = '%1 = Number of migrated files, %2 = Number of failed migrations'; | ||
| NoFilesToMigrateLbl: Label 'No files need to be migrated.'; | ||
| ProcessingMsg: Label 'Processing #1###### attachments...', Comment = '%1 - Total Number of Attachments'; | ||
|
|
||
| local procedure LogMigrationTelemetry() | ||
| var | ||
| DAFeatureTelemetry: Codeunit "DA Feature Telemetry"; | ||
| begin | ||
| DAFeatureTelemetry.LogCompanyMigration(); | ||
| end; | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.