@khester/create-dynamics-app 1.0.8 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-dynamics-app.js +1 -1
- package/dist/index.js +140 -15
- package/dist/index.js.map +1 -1
- package/dist/utils/consultingHelpers.d.ts +13 -0
- package/dist/utils/consultingHelpers.d.ts.map +1 -0
- package/dist/utils/consultingHelpers.js +569 -0
- package/dist/utils/consultingHelpers.js.map +1 -0
- package/dist/utils/copyTemplate.d.ts.map +1 -1
- package/dist/utils/copyTemplate.js.map +1 -1
- package/dist/utils/initGit.d.ts.map +1 -1
- package/dist/utils/initGit.js.map +1 -1
- package/dist/utils/installDependencies.d.ts.map +1 -1
- package/dist/utils/installDependencies.js +3 -2
- package/dist/utils/installDependencies.js.map +1 -1
- package/dist/utils/updatePackageJson.d.ts +1 -1
- package/dist/utils/updatePackageJson.d.ts.map +1 -1
- package/dist/utils/updatePackageJson.js +11 -1
- package/dist/utils/updatePackageJson.js.map +1 -1
- package/package.json +1 -1
- package/templates/dynamics-365-starter/INTEGRATION_TEST_RESULTS.md +302 -0
- package/templates/dynamics-365-starter/PHASE_4_COMPLETION_SUMMARY.md +305 -0
- package/templates/dynamics-365-starter/README.md +566 -137
- package/templates/dynamics-365-starter/deployment/QUICKSTART-MAC.md +507 -0
- package/templates/dynamics-365-starter/deployment/QUICKSTART-WINDOWS.md +372 -0
- package/templates/dynamics-365-starter/deployment/README.md +484 -0
- package/templates/dynamics-365-starter/deployment/pipelines/README.md +375 -0
- package/templates/dynamics-365-starter/deployment/pipelines/azure-pipelines.yml +330 -0
- package/templates/dynamics-365-starter/deployment/pipelines/github-actions.yml +422 -0
- package/templates/dynamics-365-starter/deployment/pipelines/jenkins.groovy +636 -0
- package/templates/dynamics-365-starter/deployment/scripts/deploy.ps1 +417 -0
- package/templates/dynamics-365-starter/deployment/scripts/deploy.sh +582 -0
- package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.ps1 +486 -0
- package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.sh +567 -0
- package/templates/dynamics-365-starter/deployment/scripts/validate-setup.ps1 +703 -0
- package/templates/dynamics-365-starter/deployment/scripts/validate-setup.sh +671 -0
- package/templates/dynamics-365-starter/docs/ARCHITECTURE_OVERVIEW.md +506 -0
- package/templates/dynamics-365-starter/docs/BEST_PRACTICES.md +723 -0
- package/templates/dynamics-365-starter/docs/MIGRATION_GUIDE.md +447 -0
- package/templates/dynamics-365-starter/docs/team-standards/README.md +273 -0
- package/templates/dynamics-365-starter/docs/team-standards/client-onboarding.md +577 -0
- package/templates/dynamics-365-starter/docs/team-standards/code-review-checklist.md +359 -0
- package/templates/dynamics-365-starter/docs/team-standards/coding-standards.md +700 -0
- package/templates/dynamics-365-starter/docs/team-standards/cross-platform-team-guide.md +736 -0
- package/templates/dynamics-365-starter/docs/team-standards/development-workflows.md +727 -0
- package/templates/dynamics-365-starter/docs/troubleshooting/common-errors.md +758 -0
- package/templates/dynamics-365-starter/docs/troubleshooting/platform-specific-issues.md +878 -0
- package/templates/dynamics-365-starter/package.json +22 -1
- package/templates/dynamics-365-starter/public/index.html +8 -11
- package/templates/dynamics-365-starter/scripts/custom-build.js +255 -0
- package/templates/dynamics-365-starter/src/client-project-template/README.md +234 -0
- package/templates/dynamics-365-starter/src/client-project-template/config/client.template.json +114 -0
- package/templates/dynamics-365-starter/src/client-project-template/config/environments/template.json +186 -0
- package/templates/dynamics-365-starter/src/client-project-template/scripts/client-setup.js +667 -0
- package/templates/dynamics-365-starter/src/components/AccountForm.css +71 -0
- package/templates/dynamics-365-starter/src/components/AccountForm.tsx +541 -0
- package/templates/dynamics-365-starter/src/components/AccountManagement.css +86 -0
- package/templates/dynamics-365-starter/src/components/AccountManagement.tsx +370 -0
- package/templates/dynamics-365-starter/src/components/ContactForm.tsx +149 -63
- package/templates/dynamics-365-starter/src/components/ContactManagement.tsx +153 -63
- package/templates/dynamics-365-starter/src/components/Logging/LogDialog.tsx +291 -0
- package/templates/dynamics-365-starter/src/components/Logging/LoggingContext.tsx +166 -0
- package/templates/dynamics-365-starter/src/components/Logging/LoggingDebugPanel.css +192 -0
- package/templates/dynamics-365-starter/src/components/Logging/LoggingDebugPanel.tsx +177 -0
- package/templates/dynamics-365-starter/src/components/Logging/LoggingProvider.tsx +3 -0
- package/templates/dynamics-365-starter/src/components/Logging/logger.ts +193 -0
- package/templates/dynamics-365-starter/src/constants/account.ts +410 -0
- package/templates/dynamics-365-starter/src/constants/contact.ts +362 -0
- package/templates/dynamics-365-starter/src/examples/README.md +52 -0
- package/templates/dynamics-365-starter/src/examples/component-examples/opportunity-management.tsx +625 -0
- package/templates/dynamics-365-starter/src/examples/entity-examples/opportunity-model.ts +545 -0
- package/templates/dynamics-365-starter/src/examples/integration-examples/custom-pcf-wrapper.tsx +722 -0
- package/templates/dynamics-365-starter/src/examples/workflow-examples/sales-workflow.ts +662 -0
- package/templates/dynamics-365-starter/src/index.tsx +107 -19
- package/templates/dynamics-365-starter/src/models/Account.ts +480 -0
- package/templates/dynamics-365-starter/src/models/BaseEntity.ts +204 -0
- package/templates/dynamics-365-starter/src/models/Contact.ts +580 -0
- package/templates/dynamics-365-starter/src/page-templates/EntityDashboard.tsx +519 -0
- package/templates/dynamics-365-starter/src/page-templates/EntityDetailPage.tsx +456 -0
- package/templates/dynamics-365-starter/src/page-templates/EntityListPage.tsx +406 -0
- package/templates/dynamics-365-starter/src/page-templates/RelatedEntitiesPage.tsx +578 -0
- package/templates/dynamics-365-starter/src/page-templates/SearchPage.tsx +629 -0
- package/templates/dynamics-365-starter/src/pcf/ContactControlWrapper.tsx +75 -22
- package/templates/dynamics-365-starter/src/pcf/MultiEntityControlWrapper.tsx +205 -0
- package/templates/dynamics-365-starter/src/providers/DynamicsProvider.tsx +297 -80
- package/templates/dynamics-365-starter/src/services/MockApiService.ts +260 -0
- package/templates/dynamics-365-starter/src/services/ServiceFactory.ts +65 -0
- package/templates/dynamics-365-starter/src/services/XrmApiService.ts +213 -0
- package/templates/dynamics-365-starter/src/styles/index.css +74 -7
- package/templates/dynamics-365-starter/tools/entity-generator/index.js +168 -0
- package/templates/dynamics-365-starter/tools/entity-generator/templates/constants.template.ts +124 -0
- package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.css +283 -0
- package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.tsx +275 -0
- package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.css +204 -0
- package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.tsx +413 -0
- package/templates/dynamics-365-starter/tools/entity-generator/templates/model.template.ts +250 -0
- package/templates/dynamics-365-starter/tools/metadata-sync/d365-client.js +410 -0
- package/templates/dynamics-365-starter/tools/metadata-sync/index.js +512 -0
- package/templates/dynamics-365-starter/tools/metadata-sync/type-generator.js +675 -0
- package/templates/dynamics-365-starter/tsconfig.json +11 -8
- package/templates/dynamics-365-starter/webpack.config.js +8 -9
- package/templates/power-pages-starter/README.md +7 -1
- package/templates/power-pages-starter/public/index.html +8 -11
- package/templates/power-pages-starter/src/components/ContactForm.tsx +60 -41
- package/templates/power-pages-starter/src/index.tsx +3 -3
- package/templates/power-pages-starter/src/providers/PowerPagesProvider.tsx +46 -23
- package/templates/power-pages-starter/tsconfig.json +3 -9
- package/templates/power-pages-starter/webpack.config.js +8 -3
|
@@ -1,27 +1,112 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState, useCallback } from 'react';
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { Pivot, DynamicsPivotItemProps } from '@khester/dynamics-ui-components';
|
|
3
4
|
import { ContactManagement } from './components/ContactManagement';
|
|
5
|
+
import { AccountManagement } from './components/AccountManagement';
|
|
4
6
|
import { DynamicsProvider } from './providers/DynamicsProvider';
|
|
7
|
+
import { LoggingProvider } from './components/Logging/LoggingProvider';
|
|
8
|
+
import { LoggingDebugPanel } from './components/Logging/LoggingDebugPanel';
|
|
9
|
+
import { Logger } from './components/Logging/logger';
|
|
5
10
|
import './styles/index.css';
|
|
6
11
|
|
|
7
12
|
const App: React.FC = () => {
|
|
13
|
+
const [selectedTab, setSelectedTab] = useState<'contacts' | 'accounts'>(
|
|
14
|
+
'contacts'
|
|
15
|
+
);
|
|
16
|
+
const [showDebugPanel, setShowDebugPanel] = useState(false);
|
|
17
|
+
|
|
18
|
+
const handleTabChange = useCallback(
|
|
19
|
+
(item: DynamicsPivotItemProps) => {
|
|
20
|
+
const newTab = item.itemKey as 'contacts' | 'accounts';
|
|
21
|
+
Logger.userAction(
|
|
22
|
+
'Tab changed in main application',
|
|
23
|
+
{ from: selectedTab, to: newTab },
|
|
24
|
+
'App.handleTabChange'
|
|
25
|
+
);
|
|
26
|
+
setSelectedTab(newTab);
|
|
27
|
+
},
|
|
28
|
+
[selectedTab]
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const toggleDebugPanel = useCallback(() => {
|
|
32
|
+
setShowDebugPanel((prev) => !prev);
|
|
33
|
+
Logger.userAction(
|
|
34
|
+
showDebugPanel ? 'Debug panel closed' : 'Debug panel opened',
|
|
35
|
+
{},
|
|
36
|
+
'App.toggleDebugPanel'
|
|
37
|
+
);
|
|
38
|
+
}, [showDebugPanel]);
|
|
39
|
+
|
|
40
|
+
const renderContent = () => {
|
|
41
|
+
switch (selectedTab) {
|
|
42
|
+
case 'contacts':
|
|
43
|
+
return <ContactManagement />;
|
|
44
|
+
case 'accounts':
|
|
45
|
+
return <AccountManagement />;
|
|
46
|
+
default:
|
|
47
|
+
return <ContactManagement />;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
8
51
|
return (
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
52
|
+
<LoggingProvider>
|
|
53
|
+
<DynamicsProvider>
|
|
54
|
+
<div className="app">
|
|
55
|
+
<header className="app-header">
|
|
56
|
+
<div className="app-header__content">
|
|
57
|
+
<div className="app-header__title">
|
|
58
|
+
<h1>Dynamics 365 Management Console</h1>
|
|
59
|
+
<p>Built with Dynamics UI Kit - Enhanced Template</p>
|
|
60
|
+
</div>
|
|
61
|
+
<div className="app-header__actions">
|
|
62
|
+
<button
|
|
63
|
+
className="debug-toggle-btn"
|
|
64
|
+
onClick={toggleDebugPanel}
|
|
65
|
+
title="Toggle Debug Panel"
|
|
66
|
+
>
|
|
67
|
+
🐛 Debug
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</header>
|
|
72
|
+
|
|
73
|
+
<nav className="app-navigation">
|
|
74
|
+
<Pivot
|
|
75
|
+
items={[
|
|
76
|
+
{
|
|
77
|
+
headerText: 'Contacts',
|
|
78
|
+
itemKey: 'contacts',
|
|
79
|
+
itemIcon: 'Contact',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
headerText: 'Accounts',
|
|
83
|
+
itemKey: 'accounts',
|
|
84
|
+
itemIcon: 'Building',
|
|
85
|
+
},
|
|
86
|
+
]}
|
|
87
|
+
selectedKey={selectedTab}
|
|
88
|
+
onItemClick={handleTabChange}
|
|
89
|
+
linkFormat="tabs"
|
|
90
|
+
/>
|
|
91
|
+
</nav>
|
|
92
|
+
|
|
93
|
+
<main className="app-main">{renderContent()}</main>
|
|
94
|
+
|
|
95
|
+
<footer className="app-footer">
|
|
96
|
+
<p>
|
|
97
|
+
© 2024 Your Organization. Powered by Dynamics UI Kit v1.0
|
|
98
|
+
</p>
|
|
99
|
+
</footer>
|
|
100
|
+
|
|
101
|
+
{/* Debug Panel */}
|
|
102
|
+
{showDebugPanel && (
|
|
103
|
+
<div className="debug-panel-overlay">
|
|
104
|
+
<LoggingDebugPanel onClose={() => setShowDebugPanel(false)} />
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
</div>
|
|
108
|
+
</DynamicsProvider>
|
|
109
|
+
</LoggingProvider>
|
|
25
110
|
);
|
|
26
111
|
};
|
|
27
112
|
|
|
@@ -35,6 +120,9 @@ if (typeof document !== 'undefined') {
|
|
|
35
120
|
}
|
|
36
121
|
|
|
37
122
|
// Export for PCF integration
|
|
38
|
-
export { App, ContactManagement };
|
|
123
|
+
export { App, ContactManagement, AccountManagement };
|
|
39
124
|
export * from './components/ContactManagement';
|
|
40
|
-
export * from './
|
|
125
|
+
export * from './components/AccountManagement';
|
|
126
|
+
export * from './providers/DynamicsProvider';
|
|
127
|
+
export * from './pcf/ContactControlWrapper';
|
|
128
|
+
export * from './pcf/MultiEntityControlWrapper';
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { IApiService } from '@khester/dynamics-ui-api-client';
|
|
2
|
+
import { BaseEntity } from './BaseEntity';
|
|
3
|
+
import { AccountConstants } from '../constants/account';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Interface for Account entity
|
|
7
|
+
*/
|
|
8
|
+
export interface IAccount {
|
|
9
|
+
accountid?: string;
|
|
10
|
+
name: string;
|
|
11
|
+
accountnumber?: string;
|
|
12
|
+
emailaddress1?: string;
|
|
13
|
+
emailaddress2?: string;
|
|
14
|
+
emailaddress3?: string;
|
|
15
|
+
telephone1?: string;
|
|
16
|
+
telephone2?: string;
|
|
17
|
+
telephone3?: string;
|
|
18
|
+
fax?: string;
|
|
19
|
+
websiteurl?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
revenue?: number;
|
|
22
|
+
numberofemployees?: number;
|
|
23
|
+
sic?: string;
|
|
24
|
+
tickersymbol?: string;
|
|
25
|
+
stockexchange?: string;
|
|
26
|
+
ownershipcode?: number;
|
|
27
|
+
industrycode?: number;
|
|
28
|
+
accountcategorycode?: number;
|
|
29
|
+
accountclassificationcode?: number;
|
|
30
|
+
accountratingcode?: number;
|
|
31
|
+
customersizecode?: number;
|
|
32
|
+
customertypecode?: number;
|
|
33
|
+
territorycode?: number;
|
|
34
|
+
marketcap?: number;
|
|
35
|
+
sharesoutstanding?: number;
|
|
36
|
+
creditlimit?: number;
|
|
37
|
+
creditonhold?: boolean;
|
|
38
|
+
donotbulkemail?: boolean;
|
|
39
|
+
donotemail?: boolean;
|
|
40
|
+
donotfax?: boolean;
|
|
41
|
+
donotphone?: boolean;
|
|
42
|
+
donotpostalmail?: boolean;
|
|
43
|
+
donotsendmm?: boolean;
|
|
44
|
+
marketingonly?: boolean;
|
|
45
|
+
preferredcontactmethodcode?: number;
|
|
46
|
+
paymenttermscode?: number;
|
|
47
|
+
shippingmethodcode?: number;
|
|
48
|
+
primarycontactid?: string;
|
|
49
|
+
parentaccountid?: string;
|
|
50
|
+
address1_line1?: string;
|
|
51
|
+
address1_line2?: string;
|
|
52
|
+
address1_line3?: string;
|
|
53
|
+
address1_city?: string;
|
|
54
|
+
address1_stateorprovince?: string;
|
|
55
|
+
address1_postalcode?: string;
|
|
56
|
+
address1_country?: string;
|
|
57
|
+
address1_county?: string;
|
|
58
|
+
address1_latitude?: number;
|
|
59
|
+
address1_longitude?: number;
|
|
60
|
+
address1_telephone1?: string;
|
|
61
|
+
address1_telephone2?: string;
|
|
62
|
+
address1_telephone3?: string;
|
|
63
|
+
address1_fax?: string;
|
|
64
|
+
address1_addresstypecode?: string;
|
|
65
|
+
address1_name?: string;
|
|
66
|
+
address1_primarycontactname?: string;
|
|
67
|
+
address1_shippingmethodcode?: number;
|
|
68
|
+
statecode?: number;
|
|
69
|
+
statuscode?: number;
|
|
70
|
+
createdon?: string;
|
|
71
|
+
modifiedon?: string;
|
|
72
|
+
createdby?: string;
|
|
73
|
+
modifiedby?: string;
|
|
74
|
+
ownerid?: string;
|
|
75
|
+
transactioncurrencyid?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Account entity model class
|
|
80
|
+
*/
|
|
81
|
+
export class Account extends BaseEntity implements IAccount {
|
|
82
|
+
accountid?: string;
|
|
83
|
+
name: string;
|
|
84
|
+
accountnumber?: string;
|
|
85
|
+
emailaddress1?: string;
|
|
86
|
+
emailaddress2?: string;
|
|
87
|
+
emailaddress3?: string;
|
|
88
|
+
telephone1?: string;
|
|
89
|
+
telephone2?: string;
|
|
90
|
+
telephone3?: string;
|
|
91
|
+
fax?: string;
|
|
92
|
+
websiteurl?: string;
|
|
93
|
+
description?: string;
|
|
94
|
+
revenue?: number;
|
|
95
|
+
numberofemployees?: number;
|
|
96
|
+
sic?: string;
|
|
97
|
+
tickersymbol?: string;
|
|
98
|
+
stockexchange?: string;
|
|
99
|
+
ownershipcode?: number;
|
|
100
|
+
industrycode?: number;
|
|
101
|
+
accountcategorycode?: number;
|
|
102
|
+
accountclassificationcode?: number;
|
|
103
|
+
accountratingcode?: number;
|
|
104
|
+
customersizecode?: number;
|
|
105
|
+
customertypecode?: number;
|
|
106
|
+
territorycode?: number;
|
|
107
|
+
marketcap?: number;
|
|
108
|
+
sharesoutstanding?: number;
|
|
109
|
+
creditlimit?: number;
|
|
110
|
+
creditonhold?: boolean;
|
|
111
|
+
donotbulkemail?: boolean;
|
|
112
|
+
donotemail?: boolean;
|
|
113
|
+
donotfax?: boolean;
|
|
114
|
+
donotphone?: boolean;
|
|
115
|
+
donotpostalmail?: boolean;
|
|
116
|
+
donotsendmm?: boolean;
|
|
117
|
+
marketingonly?: boolean;
|
|
118
|
+
preferredcontactmethodcode?: number;
|
|
119
|
+
paymenttermscode?: number;
|
|
120
|
+
shippingmethodcode?: number;
|
|
121
|
+
primarycontactid?: string;
|
|
122
|
+
parentaccountid?: string;
|
|
123
|
+
address1_line1?: string;
|
|
124
|
+
address1_line2?: string;
|
|
125
|
+
address1_line3?: string;
|
|
126
|
+
address1_city?: string;
|
|
127
|
+
address1_stateorprovince?: string;
|
|
128
|
+
address1_postalcode?: string;
|
|
129
|
+
address1_country?: string;
|
|
130
|
+
address1_county?: string;
|
|
131
|
+
address1_latitude?: number;
|
|
132
|
+
address1_longitude?: number;
|
|
133
|
+
address1_telephone1?: string;
|
|
134
|
+
address1_telephone2?: string;
|
|
135
|
+
address1_telephone3?: string;
|
|
136
|
+
address1_fax?: string;
|
|
137
|
+
address1_addresstypecode?: string;
|
|
138
|
+
address1_name?: string;
|
|
139
|
+
address1_primarycontactname?: string;
|
|
140
|
+
address1_shippingmethodcode?: number;
|
|
141
|
+
statecode?: number;
|
|
142
|
+
statuscode?: number;
|
|
143
|
+
createdon?: string;
|
|
144
|
+
modifiedon?: string;
|
|
145
|
+
createdby?: string;
|
|
146
|
+
modifiedby?: string;
|
|
147
|
+
ownerid?: string;
|
|
148
|
+
transactioncurrencyid?: string;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Constructor for Account entity
|
|
152
|
+
* @param account The account data
|
|
153
|
+
*/
|
|
154
|
+
constructor(account: IAccount) {
|
|
155
|
+
super();
|
|
156
|
+
this.accountid = account.accountid;
|
|
157
|
+
this.name = account.name;
|
|
158
|
+
this.accountnumber = account.accountnumber;
|
|
159
|
+
this.emailaddress1 = account.emailaddress1;
|
|
160
|
+
this.emailaddress2 = account.emailaddress2;
|
|
161
|
+
this.emailaddress3 = account.emailaddress3;
|
|
162
|
+
this.telephone1 = account.telephone1;
|
|
163
|
+
this.telephone2 = account.telephone2;
|
|
164
|
+
this.telephone3 = account.telephone3;
|
|
165
|
+
this.fax = account.fax;
|
|
166
|
+
this.websiteurl = account.websiteurl;
|
|
167
|
+
this.description = account.description;
|
|
168
|
+
this.revenue = account.revenue;
|
|
169
|
+
this.numberofemployees = account.numberofemployees;
|
|
170
|
+
this.sic = account.sic;
|
|
171
|
+
this.tickersymbol = account.tickersymbol;
|
|
172
|
+
this.stockexchange = account.stockexchange;
|
|
173
|
+
this.ownershipcode = account.ownershipcode;
|
|
174
|
+
this.industrycode = account.industrycode;
|
|
175
|
+
this.accountcategorycode = account.accountcategorycode;
|
|
176
|
+
this.accountclassificationcode = account.accountclassificationcode;
|
|
177
|
+
this.accountratingcode = account.accountratingcode;
|
|
178
|
+
this.customersizecode = account.customersizecode;
|
|
179
|
+
this.customertypecode = account.customertypecode;
|
|
180
|
+
this.territorycode = account.territorycode;
|
|
181
|
+
this.marketcap = account.marketcap;
|
|
182
|
+
this.sharesoutstanding = account.sharesoutstanding;
|
|
183
|
+
this.creditlimit = account.creditlimit;
|
|
184
|
+
this.creditonhold = account.creditonhold;
|
|
185
|
+
this.donotbulkemail = account.donotbulkemail;
|
|
186
|
+
this.donotemail = account.donotemail;
|
|
187
|
+
this.donotfax = account.donotfax;
|
|
188
|
+
this.donotphone = account.donotphone;
|
|
189
|
+
this.donotpostalmail = account.donotpostalmail;
|
|
190
|
+
this.donotsendmm = account.donotsendmm;
|
|
191
|
+
this.marketingonly = account.marketingonly;
|
|
192
|
+
this.preferredcontactmethodcode = account.preferredcontactmethodcode;
|
|
193
|
+
this.paymenttermscode = account.paymenttermscode;
|
|
194
|
+
this.shippingmethodcode = account.shippingmethodcode;
|
|
195
|
+
this.primarycontactid = account.primarycontactid;
|
|
196
|
+
this.parentaccountid = account.parentaccountid;
|
|
197
|
+
this.address1_line1 = account.address1_line1;
|
|
198
|
+
this.address1_line2 = account.address1_line2;
|
|
199
|
+
this.address1_line3 = account.address1_line3;
|
|
200
|
+
this.address1_city = account.address1_city;
|
|
201
|
+
this.address1_stateorprovince = account.address1_stateorprovince;
|
|
202
|
+
this.address1_postalcode = account.address1_postalcode;
|
|
203
|
+
this.address1_country = account.address1_country;
|
|
204
|
+
this.address1_county = account.address1_county;
|
|
205
|
+
this.address1_latitude = account.address1_latitude;
|
|
206
|
+
this.address1_longitude = account.address1_longitude;
|
|
207
|
+
this.address1_telephone1 = account.address1_telephone1;
|
|
208
|
+
this.address1_telephone2 = account.address1_telephone2;
|
|
209
|
+
this.address1_telephone3 = account.address1_telephone3;
|
|
210
|
+
this.address1_fax = account.address1_fax;
|
|
211
|
+
this.address1_addresstypecode = account.address1_addresstypecode;
|
|
212
|
+
this.address1_name = account.address1_name;
|
|
213
|
+
this.address1_primarycontactname = account.address1_primarycontactname;
|
|
214
|
+
this.address1_shippingmethodcode = account.address1_shippingmethodcode;
|
|
215
|
+
this.statecode = account.statecode;
|
|
216
|
+
this.statuscode = account.statuscode;
|
|
217
|
+
this.createdon = account.createdon;
|
|
218
|
+
this.modifiedon = account.modifiedon;
|
|
219
|
+
this.createdby = account.createdby;
|
|
220
|
+
this.modifiedby = account.modifiedby;
|
|
221
|
+
this.ownerid = account.ownerid;
|
|
222
|
+
this.transactioncurrencyid = account.transactioncurrencyid;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Validates the account instance
|
|
227
|
+
* @returns True if valid, throws error if invalid
|
|
228
|
+
*/
|
|
229
|
+
validate(): boolean {
|
|
230
|
+
if (!this.name || this.name.trim().length === 0) {
|
|
231
|
+
throw new Error('Account name is required');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (this.name.length > 160) {
|
|
235
|
+
throw new Error('Account name cannot exceed 160 characters');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (this.accountnumber && this.accountnumber.length > 20) {
|
|
239
|
+
throw new Error('Account number cannot exceed 20 characters');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (this.emailaddress1 && !this.isValidEmail(this.emailaddress1)) {
|
|
243
|
+
throw new Error('Invalid primary email address format');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (this.emailaddress2 && !this.isValidEmail(this.emailaddress2)) {
|
|
247
|
+
throw new Error('Invalid secondary email address format');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (this.emailaddress3 && !this.isValidEmail(this.emailaddress3)) {
|
|
251
|
+
throw new Error('Invalid tertiary email address format');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (this.websiteurl && !this.isValidUrl(this.websiteurl)) {
|
|
255
|
+
throw new Error('Invalid website URL format');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (this.revenue !== undefined && this.revenue < 0) {
|
|
259
|
+
throw new Error('Revenue cannot be negative');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (this.numberofemployees !== undefined && this.numberofemployees < 0) {
|
|
263
|
+
throw new Error('Number of employees cannot be negative');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (this.creditlimit !== undefined && this.creditlimit < 0) {
|
|
267
|
+
throw new Error('Credit limit cannot be negative');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Creates a new account record
|
|
275
|
+
* @param apiService The API service instance
|
|
276
|
+
* @param account The account to create
|
|
277
|
+
* @returns Promise resolving to the created account
|
|
278
|
+
*/
|
|
279
|
+
public static async create(
|
|
280
|
+
apiService: IApiService,
|
|
281
|
+
account: Account
|
|
282
|
+
): Promise<Account> {
|
|
283
|
+
const loggerContext = 'Account.create';
|
|
284
|
+
console.log(`${loggerContext}: Creating new account`, {
|
|
285
|
+
name: account.name,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return await this.createEntity<Account>(
|
|
289
|
+
apiService,
|
|
290
|
+
account,
|
|
291
|
+
AccountConstants.EntityCollectionName,
|
|
292
|
+
loggerContext
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Updates an existing account record
|
|
298
|
+
* @param apiService The API service instance
|
|
299
|
+
* @param account The account to update
|
|
300
|
+
* @returns Promise resolving to the updated account
|
|
301
|
+
*/
|
|
302
|
+
public static async update(
|
|
303
|
+
apiService: IApiService,
|
|
304
|
+
account: Account
|
|
305
|
+
): Promise<Account> {
|
|
306
|
+
const loggerContext = 'Account.update';
|
|
307
|
+
|
|
308
|
+
if (!account.accountid) {
|
|
309
|
+
throw new Error('Account ID is required for update');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
console.log(`${loggerContext}: Updating account`, {
|
|
313
|
+
accountid: account.accountid,
|
|
314
|
+
name: account.name,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
return await this.updateEntity<Account>(
|
|
318
|
+
apiService,
|
|
319
|
+
account.accountid,
|
|
320
|
+
account,
|
|
321
|
+
AccountConstants.EntityCollectionName,
|
|
322
|
+
AccountConstants.PrimaryKey,
|
|
323
|
+
loggerContext
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Deletes an account record
|
|
329
|
+
* @param apiService The API service instance
|
|
330
|
+
* @param accountId The ID of the account to delete
|
|
331
|
+
* @returns Promise resolving when deletion is complete
|
|
332
|
+
*/
|
|
333
|
+
public static async delete(
|
|
334
|
+
apiService: IApiService,
|
|
335
|
+
accountId: string
|
|
336
|
+
): Promise<void> {
|
|
337
|
+
const loggerContext = 'Account.delete';
|
|
338
|
+
console.log(`${loggerContext}: Deleting account`, { accountid: accountId });
|
|
339
|
+
|
|
340
|
+
return await this.deleteEntity(
|
|
341
|
+
apiService,
|
|
342
|
+
accountId,
|
|
343
|
+
AccountConstants.EntityCollectionName,
|
|
344
|
+
loggerContext
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Retrieves accounts based on a filter
|
|
350
|
+
* @param apiService The API service instance
|
|
351
|
+
* @param filter Optional FetchXML filter string
|
|
352
|
+
* @returns Promise resolving to an array of accounts
|
|
353
|
+
*/
|
|
354
|
+
public static async retrieveByFilter(
|
|
355
|
+
apiService: IApiService,
|
|
356
|
+
filter?: string
|
|
357
|
+
): Promise<Account[]> {
|
|
358
|
+
const loggerContext = 'Account.retrieveByFilter';
|
|
359
|
+
console.log(`${loggerContext}: Retrieving accounts with filter`, {
|
|
360
|
+
filter,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const attributes = [
|
|
364
|
+
AccountConstants.PrimaryKey,
|
|
365
|
+
AccountConstants.PrimaryName,
|
|
366
|
+
AccountConstants.AccountNumber,
|
|
367
|
+
AccountConstants.EMailAddress1,
|
|
368
|
+
AccountConstants.Telephone1,
|
|
369
|
+
AccountConstants.Address1_City,
|
|
370
|
+
AccountConstants.Address1_StateOrProvince,
|
|
371
|
+
AccountConstants.Address1_Country,
|
|
372
|
+
AccountConstants.Revenue,
|
|
373
|
+
AccountConstants.NumberOfEmployees,
|
|
374
|
+
AccountConstants.IndustryCode,
|
|
375
|
+
AccountConstants.OwnershipCode,
|
|
376
|
+
AccountConstants.WebSiteURL,
|
|
377
|
+
AccountConstants.Description,
|
|
378
|
+
AccountConstants.StateCode,
|
|
379
|
+
AccountConstants.StatusCode,
|
|
380
|
+
AccountConstants.CreatedOn,
|
|
381
|
+
AccountConstants.ModifiedOn,
|
|
382
|
+
];
|
|
383
|
+
|
|
384
|
+
const fetchXml = this.buildFetchXml(
|
|
385
|
+
AccountConstants.EntityName,
|
|
386
|
+
attributes,
|
|
387
|
+
filter,
|
|
388
|
+
{ attribute: AccountConstants.PrimaryName }
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
return await this.retrieveEntitiesByFilter<Account>(
|
|
392
|
+
apiService,
|
|
393
|
+
AccountConstants.EntityCollectionName,
|
|
394
|
+
fetchXml,
|
|
395
|
+
Account,
|
|
396
|
+
loggerContext
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Retrieves a single account by ID
|
|
402
|
+
* @param apiService The API service instance
|
|
403
|
+
* @param accountId The account ID
|
|
404
|
+
* @returns Promise resolving to the account or null if not found
|
|
405
|
+
*/
|
|
406
|
+
public static async retrieveById(
|
|
407
|
+
apiService: IApiService,
|
|
408
|
+
accountId: string
|
|
409
|
+
): Promise<Account | null> {
|
|
410
|
+
const loggerContext = 'Account.retrieveById';
|
|
411
|
+
console.log(`${loggerContext}: Retrieving account by ID`, {
|
|
412
|
+
accountid: accountId,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const filter = `<filter type="and">
|
|
416
|
+
<condition attribute="${AccountConstants.PrimaryKey}" operator="eq" value="${this.escapeXml(accountId)}" />
|
|
417
|
+
</filter>`;
|
|
418
|
+
|
|
419
|
+
const accounts = await this.retrieveByFilter(apiService, filter);
|
|
420
|
+
return accounts.length > 0 ? accounts[0] : null;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Retrieves accounts by name (partial match)
|
|
425
|
+
* @param apiService The API service instance
|
|
426
|
+
* @param name The name to search for
|
|
427
|
+
* @returns Promise resolving to an array of matching accounts
|
|
428
|
+
*/
|
|
429
|
+
public static async retrieveByName(
|
|
430
|
+
apiService: IApiService,
|
|
431
|
+
name: string
|
|
432
|
+
): Promise<Account[]> {
|
|
433
|
+
const loggerContext = 'Account.retrieveByName';
|
|
434
|
+
console.log(`${loggerContext}: Retrieving accounts by name`, { name });
|
|
435
|
+
|
|
436
|
+
const filter = `<filter type="and">
|
|
437
|
+
<condition attribute="${AccountConstants.PrimaryName}" operator="like" value="%${this.escapeXml(name)}%" />
|
|
438
|
+
</filter>`;
|
|
439
|
+
|
|
440
|
+
return await this.retrieveByFilter(apiService, filter);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Retrieves active accounts only
|
|
445
|
+
* @param apiService The API service instance
|
|
446
|
+
* @returns Promise resolving to an array of active accounts
|
|
447
|
+
*/
|
|
448
|
+
public static async retrieveActiveAccounts(
|
|
449
|
+
apiService: IApiService
|
|
450
|
+
): Promise<Account[]> {
|
|
451
|
+
const loggerContext = 'Account.retrieveActiveAccounts';
|
|
452
|
+
console.log(`${loggerContext}: Retrieving active accounts`);
|
|
453
|
+
|
|
454
|
+
const filter = `<filter type="and">
|
|
455
|
+
<condition attribute="${AccountConstants.StateCode}" operator="eq" value="0" />
|
|
456
|
+
</filter>`;
|
|
457
|
+
|
|
458
|
+
return await this.retrieveByFilter(apiService, filter);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Helper method to validate email format
|
|
463
|
+
*/
|
|
464
|
+
private isValidEmail(email: string): boolean {
|
|
465
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
466
|
+
return emailRegex.test(email);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Helper method to validate URL format
|
|
471
|
+
*/
|
|
472
|
+
private isValidUrl(url: string): boolean {
|
|
473
|
+
try {
|
|
474
|
+
new URL(url);
|
|
475
|
+
return true;
|
|
476
|
+
} catch {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|