Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,59 @@
import { businessCentralAuth } from '../../';
import { createAction, Property } from '@activepieces/pieces-framework';
import { commonProps, formatRecordFields } from '../common';
import { makeClient } from '../common/client';
import { ACTION_ENTITY_DROPDOWN_OPTIONS } from '../common/constants';
export const createRecordAction = createAction({
auth: businessCentralAuth,
name: 'create-record',
displayName: 'Create Record',
description: 'Creates a new record.',
props: {
company_id: commonProps.company_id,
record_type: Property.StaticDropdown({
displayName: 'Record Type',
required: true,
options: {
disabled: false,
options: ACTION_ENTITY_DROPDOWN_OPTIONS,
},
}),
record_fields: commonProps.record_fields,
},
async run(context) {
const companyId = context.propsValue.company_id;
const recordType = context.propsValue.record_type;
const recordFields = context.propsValue.record_fields;
const formattedRecordFields = formatRecordFields(recordFields, recordType);
const client = makeClient(context.auth);
let endpoint;
switch (recordType) {
case 'itemVariants':
endpoint = `/companies(${companyId})/items(${recordFields['itemId']})/${recordType}`;
delete formattedRecordFields['itemId'];
break;
case 'salesInvoiceLines':
endpoint = `/companies(${companyId})/salesInvoices(${recordFields['salesInvoiceId']})/${recordType}`;
delete formattedRecordFields['salesInvoiceId'];
break;
case 'salesOrderLines':
endpoint = `/companies(${companyId})/salesOrders(${recordFields['salesOrderId']})/${recordType}`;
delete formattedRecordFields['salesOrderId'];
break;
case 'salesQuoteLines':
endpoint = `/companies(${companyId})/salesQuotes(${recordFields['salesQuoteId']})/${recordType}`;
delete formattedRecordFields['salesQuoteId'];
break;
default:
endpoint = `/companies(${companyId})/${recordType}`;
break;
}
return await client.createRecord(endpoint, formattedRecordFields);
},
});

View File

@@ -0,0 +1,34 @@
import { businessCentralAuth } from '../../';
import { createAction, Property } from '@activepieces/pieces-framework';
import { commonProps } from '../common';
import { makeClient } from '../common/client';
import { ACTION_ENTITY_DROPDOWN_OPTIONS } from '../common/constants';
export const deleteRecordAction = createAction({
auth: businessCentralAuth,
name: 'delete-record',
displayName: 'Delete Record',
description: 'Deletes an existing record.',
props: {
company_id: commonProps.company_id,
record_type: Property.StaticDropdown({
displayName: 'Record Type',
required: true,
options: {
disabled: false,
options: ACTION_ENTITY_DROPDOWN_OPTIONS,
},
}),
record_id: commonProps.record_id,
},
async run(context) {
const companyId = context.propsValue.company_id;
const recordType = context.propsValue.record_type;
const recordId = context.propsValue.record_id;
const client = makeClient(context.auth);
const endpoint = `/companies(${companyId})/${recordType}(${recordId})`;
return await client.deleteRecord(endpoint);
},
});

View File

@@ -0,0 +1,35 @@
import { businessCentralAuth } from '../../';
import { createAction, Property } from '@activepieces/pieces-framework';
import { commonProps } from '../common';
import { makeClient } from '../common/client';
import { ACTION_ENTITY_DROPDOWN_OPTIONS } from '../common/constants';
export const getRecordAction = createAction({
auth: businessCentralAuth,
name: 'get-record',
displayName: 'Get Record',
description: `Retrieves the details of a record by it's ID.`,
props: {
company_id: commonProps.company_id,
record_type: Property.StaticDropdown({
displayName: 'Record Type',
required: true,
options: {
disabled: false,
options: ACTION_ENTITY_DROPDOWN_OPTIONS,
},
}),
record_id: commonProps.record_id,
},
async run(context) {
const companyId = context.propsValue.company_id;
const recordType = context.propsValue.record_type;
const recordId = context.propsValue.record_id;
const client = makeClient(context.auth);
const endpoint = `/companies(${companyId})/${recordType}(${recordId})`;
return await client.getRecord(endpoint);
},
});

View File

@@ -0,0 +1,46 @@
import { businessCentralAuth } from '../../';
import { createAction, Property } from '@activepieces/pieces-framework';
import { commonProps } from '../common';
import { makeClient } from '../common/client';
import { TRIGGER_ENTITY_DROPDOWN_OPTIONS } from '../common/constants';
export const searchRecordsAction = createAction({
auth: businessCentralAuth,
name: 'search-records',
displayName: 'Search Records',
description: 'Retrieves a list of records.',
props: {
company_id: commonProps.company_id,
record_type: Property.StaticDropdown({
displayName: 'Record Type',
required: true,
options: {
disabled: false,
options: TRIGGER_ENTITY_DROPDOWN_OPTIONS,
},
}),
markdown: Property.MarkDown({
value: `You can search on any and all of the property fields below. We'll return only exact matches (capitalization matters!) on all values provided.`,
}),
record_filter_fields: commonProps.record_filter_fields,
},
async run(context) {
const companyId = context.propsValue.company_id;
const recordType = context.propsValue.record_type;
const recordFilterFields = context.propsValue.record_filter_fields;
const filterFieldsArray = [];
for (const key in recordFilterFields) {
if (recordFilterFields[key]) {
filterFieldsArray.push(`${key} eq '${recordFilterFields[key]}'`);
}
}
const client = makeClient(context.auth);
return await client.filterRecords(companyId, recordType, {
$filter: filterFieldsArray.join(' and '),
});
},
});

View File

@@ -0,0 +1,60 @@
import { businessCentralAuth } from '../../';
import { createAction, Property } from '@activepieces/pieces-framework';
import { commonProps, formatRecordFields } from '../common';
import { makeClient } from '../common/client';
import { ACTION_ENTITY_DROPDOWN_OPTIONS } from '../common/constants';
export const updateRecordAction = createAction({
auth: businessCentralAuth,
name: 'update-record',
displayName: 'Update Record',
description: 'Updates an existing record.',
props: {
company_id: commonProps.company_id,
record_type: Property.StaticDropdown({
displayName: 'Record Type',
required: true,
options: {
disabled: false,
options: ACTION_ENTITY_DROPDOWN_OPTIONS,
},
}),
record_id: commonProps.record_id,
record_fields: commonProps.record_fields,
},
async run(context) {
const companyId = context.propsValue.company_id;
const recordType = context.propsValue.record_type;
const recordId = context.propsValue.record_id;
const recordFields = context.propsValue.record_fields;
const formattedRecordFields = formatRecordFields(recordFields, recordType);
const client = makeClient(context.auth);
let endpoint;
switch (recordType) {
case 'itemVariants':
endpoint = `/companies(${companyId})/items(${recordFields['itemId']})/${recordType}(${recordId})`;
delete formattedRecordFields['itemId'];
break;
case 'salesInvoiceLines':
endpoint = `/companies(${companyId})/salesInvoices(${recordFields['salesInvoiceId']})/${recordType}(${recordId})`;
delete formattedRecordFields['salesInvoiceId'];
break;
case 'salesOrderLines':
endpoint = `/companies(${companyId})/salesOrders(${recordFields['salesOrderId']})/${recordType}(${recordId})`;
delete formattedRecordFields['salesOrderId'];
break;
case 'salesQuoteLines':
endpoint = `/companies(${companyId})/salesQuotes(${recordFields['salesQuoteId']})/${recordType}(${recordId})`;
delete formattedRecordFields['salesQuoteId'];
break;
default:
endpoint = `/companies(${companyId})/${recordType}(${recordId})`;
break;
}
return await client.updateRecord(endpoint, formattedRecordFields);
},
});

View File

@@ -0,0 +1,120 @@
import { businessCentralAuth } from '../../';
import {
HttpMessageBody,
HttpMethod,
QueryParams,
httpClient,
HttpRequest,
AuthenticationType,
HttpHeaders,
} from '@activepieces/pieces-common';
import { AppConnectionValueForAuthProperty, PiecePropValueSchema } from '@activepieces/pieces-framework';
interface ListAPIResponse<T> {
'@odata.context': string;
value: Array<T>;
}
interface CompanyResponse {
id: string;
name: string;
}
export type filterParams = Record<
string,
string | number | string[] | undefined
>;
export class BusinessCentralAPIClient {
constructor(private environment: string, private accessToken: string) {}
async makeRequest<T extends HttpMessageBody>(
method: HttpMethod,
resourceUri: string,
query?: filterParams,
body: any | undefined = undefined
): Promise<T> {
const baseUrl = `https://api.businesscentral.dynamics.com/v2.0/${this.environment}/api/v2.0`;
const params: QueryParams = {};
const headers: HttpHeaders = {};
if (query) {
for (const [key, value] of Object.entries(query)) {
if (value !== null && value !== undefined) {
params[key] = String(value);
}
}
}
if (method === HttpMethod.PATCH || method === HttpMethod.DELETE) {
headers['If-Match'] = '*';
}
const request: HttpRequest = {
method: method,
url: baseUrl + resourceUri,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: this.accessToken,
},
headers,
queryParams: params,
body: body,
};
const response = await httpClient.sendRequest<T>(request);
return response.body;
}
async listCompanies(): Promise<ListAPIResponse<CompanyResponse>> {
return await this.makeRequest(HttpMethod.GET, '/companies');
}
async createRecord(endpoint: string, request: Record<string, any>) {
return await this.makeRequest(
HttpMethod.POST,
endpoint,
undefined,
request
);
}
async updateRecord(endpoint: string, request: Record<string, any>) {
return await this.makeRequest(
HttpMethod.PATCH,
endpoint,
undefined,
request
);
}
async getRecord(endpoint: string) {
return await this.makeRequest(HttpMethod.GET, endpoint);
}
async deleteRecord(endpoint: string) {
return await this.makeRequest(HttpMethod.DELETE, endpoint);
}
async filterRecords(
companyId: string,
recordType: string,
params: filterParams
): Promise<ListAPIResponse<Record<string, unknown>>> {
return await this.makeRequest(
HttpMethod.GET,
`/companies(${companyId})/${recordType}`,
params
);
}
}
export function makeClient(
auth: AppConnectionValueForAuthProperty<typeof businessCentralAuth>
) {
const client = new BusinessCentralAPIClient(
auth.props?.['environment'] as string ?? '',
auth.access_token
);
return client;
}

View File

@@ -0,0 +1,98 @@
export const ACTION_ENTITY_DROPDOWN_OPTIONS = [
{
label: 'Bank Accounts',
value: 'bankAccounts',
},
{
label: 'Contacts',
value: 'contacts',
},
{
label: 'Currencies',
value: 'currencies',
},
{
label: 'Customers',
value: 'customers',
},
{
label: 'Dispute Status',
value: 'disputeStatus',
},
{
label: 'Employees',
value: 'employees',
},
{
label: 'Item Categories',
value: 'itemCategories',
},
{
label: 'Items',
value: 'items',
},
{
label: 'Item Variants',
value: 'itemVariants',
},
{
label: 'Journals',
value: 'journals',
},
{
label: 'Locations',
value: 'locations',
},
{
label: 'Payment Methods',
value: 'paymentMethods',
},
{
label: 'Payment Terms',
value: 'paymentTerms',
},
{
label: 'Projects',
value: 'projects',
},
{
label: 'Sales Invoice Lines',
value: 'salesInvoiceLines',
},
{
label: 'Sales Invoices',
value: 'salesInvoices',
},
{
label: 'Sales Order Lines',
value: 'salesOrderLines',
},
{
label: 'Sales Orders',
value: 'salesOrders',
},
{
label: 'Sales Quote Lines',
value: 'salesQuoteLines',
},
{
label: 'Sales Quotes',
value: 'salesQuotes',
},
{
label: 'Shipment Methods',
value: 'shipmentMethods',
},
{
label: 'Vendors',
value: 'vendors',
},
];
export const TRIGGER_ENTITY_DROPDOWN_OPTIONS =
ACTION_ENTITY_DROPDOWN_OPTIONS.filter(
(option) =>
!['salesQuoteLines', 'salesOrderLines', 'salesInvoiceLines'].includes(
option.value
)
);

View File

@@ -0,0 +1,376 @@
import { businessCentralAuth } from '../..';
import {
DropdownOption,
DynamicPropsValue,
PiecePropValueSchema,
Property,
} from '@activepieces/pieces-framework';
import { makeClient } from './client';
import { ACTION_ENTITY_DROPDOWN_OPTIONS } from './constants';
import { customersEntityProps } from './props/customers.entity';
import { bankAccountsEntityProps } from './props/bankAccounts.entity';
import { contactsEntityProps } from './props/contacts.entity';
import {
currenciesEntityNumberProps,
currenciesEntityProps,
} from './props/currencies.entity';
import { disputeStatusEntityProps } from './props/disputeStatus.entity';
import { employeesEntityProps } from './props/employees.entity';
import { vendorsEntityProps } from './props/vendors.entity';
import { journalsEntityProps } from './props/journals.entity';
import { locationsEntityProps } from './props/locations.entity';
import { paymentMethodsEntityProps } from './props/paymentMethods.entity';
import {
paymentTermsEntityNumberProps,
paymentTermsEntityProps,
} from './props/paymentTerms.entity';
import { projectsEntityProps } from './props/projects.entity';
import { itemCategoriesEntityProps } from './props/itemCategories.entity';
import { itemsEntityNumberProps, itemsEntityProps } from './props/items.entity';
import { itemVariantsEntityProps } from './props/itemVariants.entity';
import {
salesOrderLinesEntityNumberProps,
salesOrdersLinesEntityProps,
} from './props/salesOrderLines.entity';
import {
salesInvoiceLinesEntityNumberProps,
salesInvoiceLinesEntityProps,
} from './props/salesInvoiceLines.entity';
import {
salesQuoteLinesEntityNumberProps,
salesQuoteLinesEntityProps,
} from './props/salesQuoteLines.entity';
import { shipmentMethodsEntityProps } from './props/shipmentMethods.entity';
import { EntityProp } from './types';
import { salesInvoicesEntityProps } from './props/salesInvoices.entity';
import { salesOrdersEntityProps } from './props/salesOrders.entity';
import { salesQuotesEntityProps } from './props/salesQuotes.entity';
export const commonProps = {
company_id: Property.Dropdown({
auth: businessCentralAuth,
displayName: 'Company',
required: true,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect account first',
};
}
const authValue = auth
const client = makeClient(authValue);
const res = await client.listCompanies();
const options: DropdownOption<string>[] = [];
for (const company of res.value) {
options.push({ label: company.name, value: company.id });
}
return {
disabled: false,
options,
};
},
}),
record_id: Property.ShortText({
displayName: 'Record ID',
required: true,
}),
record_type: Property.StaticDropdown({
displayName: 'Record Type',
required: true,
options: {
disabled: false,
options: ACTION_ENTITY_DROPDOWN_OPTIONS,
},
}),
record_fields: Property.DynamicProperties({
auth: businessCentralAuth,
displayName: 'Record Fields',
refreshers: ['company_id', 'record_type'],
required: true,
props: async ({ auth, company_id, record_type }) => {
if (!auth) return {};
if (!company_id) return {};
if (!record_type) return {};
const recordType = record_type as unknown as string;
const companyId = company_id as unknown as string;
const authValue = auth
const client = makeClient(authValue);
const fields: DynamicPropsValue = {};
// fetch entity prop schema
const entitySchema = getEntityPropSchema(recordType);
for (const prop of entitySchema) {
switch (prop.type) {
case 'text':
fields[prop.name] = Property.ShortText({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
});
break;
case 'multi_text':
fields[prop.name] = Property.LongText({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
});
break;
case 'date':
fields[prop.name] = Property.DateTime({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
});
break;
case 'number':
fields[prop.name] = Property.Number({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
});
break;
case 'boolean':
fields[prop.name] = Property.Checkbox({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
});
break;
case 'static_select':
fields[prop.name] = Property.StaticDropdown({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
options: {
disabled: false,
options: prop.options ?? [],
},
});
break;
case 'static_multi_select':
fields[prop.name] = Property.StaticMultiSelectDropdown({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
options: {
disabled: false,
options: prop.options ?? [],
},
});
break;
case 'dynamic_multi_select':
case 'dynamic_select':
{
const propType =
prop.type === 'dynamic_select'
? Property.StaticDropdown
: Property.StaticMultiSelectDropdown;
const response = await client.filterRecords(
companyId,
prop.options.sourceFieldSlug,
{
$select: `${prop.options.labelField},id`,
}
);
const options: DropdownOption<string>[] = [];
for (const option of response.value) {
options.push({
label: option[prop.options.labelField] as string,
value: option['id'] as string,
});
}
fields[prop.name] = propType({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
options: {
disabled: false,
options,
},
});
}
break;
default:
break;
}
}
return fields;
},
}),
record_filter_fields: Property.DynamicProperties({
auth: businessCentralAuth,
displayName: 'Filter Fields',
refreshers: ['company_id', 'record_type'],
required: true,
props: async ({ auth, company_id, record_type }) => {
if (!auth) return {};
if (!company_id) return {};
if (!record_type) return {};
const recordType = record_type as unknown as string;
const fields: DynamicPropsValue = {};
// fetch entity prop schema
const entitySchema = getEntityPropSchema(recordType);
// currently only support text fields
for (const prop of entitySchema) {
switch (prop.type) {
case 'text':
fields[prop.name] = Property.ShortText({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
});
break;
case 'multi_text':
fields[prop.name] = Property.LongText({
displayName: prop.displayName,
description: prop.description,
required: prop.isRequired,
});
break;
default:
break;
}
}
return fields;
},
}),
};
export function formatRecordFields(
recordFields: DynamicPropsValue,
recordType: string
) {
const numberFields = [];
switch (recordType) {
case 'currencies':
numberFields.push(...currenciesEntityNumberProps);
break;
case 'items':
numberFields.push(...itemsEntityNumberProps);
break;
case 'salesInvoiceLines':
numberFields.push(...salesInvoiceLinesEntityNumberProps);
break;
case 'salesOrders':
numberFields.push(...salesOrderLinesEntityNumberProps);
break;
case 'salesOrderLines':
numberFields.push(...salesOrderLinesEntityNumberProps);
break;
case 'salesQuoteLines':
numberFields.push(...salesQuoteLinesEntityNumberProps);
break;
case 'paymentTerms':
numberFields.push(...paymentTermsEntityNumberProps);
break;
default:
break;
}
const formattedRecordFields: DynamicPropsValue = {};
for (const key in recordFields) {
if (recordFields[key] !== undefined) {
if (numberFields.includes(key)) {
formattedRecordFields[key] = Number(recordFields[key]);
} else {
formattedRecordFields[key] = recordFields[key];
}
}
}
return formattedRecordFields;
}
export function getEntityPropSchema(recordType: string): EntityProp[] {
let entitySchema: EntityProp[] = [];
// fetch entity prop schema
switch (recordType) {
case 'bankAccounts':
entitySchema = bankAccountsEntityProps;
break;
case 'contacts':
entitySchema = contactsEntityProps;
break;
case 'currencies':
entitySchema = currenciesEntityProps;
break;
case 'customers':
entitySchema = customersEntityProps;
break;
case 'disputeStatus':
entitySchema = disputeStatusEntityProps;
break;
case 'employees':
entitySchema = employeesEntityProps;
break;
case 'itemCategories':
entitySchema = itemCategoriesEntityProps;
break;
case 'items':
entitySchema = itemsEntityProps;
break;
case 'itemVariants':
entitySchema = itemVariantsEntityProps;
break;
case 'journals':
entitySchema = journalsEntityProps;
break;
case 'locations':
entitySchema = locationsEntityProps;
break;
case 'paymentTerms':
entitySchema = paymentTermsEntityProps;
break;
case 'paymentMethods':
entitySchema = paymentMethodsEntityProps;
break;
case 'projects':
entitySchema = projectsEntityProps;
break;
case 'salesInvoiceLines':
entitySchema = salesInvoiceLinesEntityProps;
break;
case 'salesInvoices':
entitySchema = salesInvoicesEntityProps;
break;
case 'salesOrderLines':
entitySchema = salesOrdersLinesEntityProps;
break;
case 'salesOrders':
entitySchema = salesOrdersEntityProps;
break;
case 'salesQuoteLines':
entitySchema = salesQuoteLinesEntityProps;
break;
case 'salesQuotes':
entitySchema = salesQuotesEntityProps;
break;
case 'shipmentMethods':
entitySchema = shipmentMethodsEntityProps;
break;
case 'vendors':
entitySchema = vendorsEntityProps;
break;
default:
break;
}
return entitySchema;
}

View File

@@ -0,0 +1,34 @@
import { EntityProp } from '../types';
export const bankAccountsEntityProps: EntityProp[] = [
{
name: 'number',
displayName: 'Number',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
type: 'text',
isRequired: false,
},
{
name: 'bankAccountNumber',
displayName: 'Bank Account Number',
type: 'text',
isRequired: false,
},
{
name: 'blocked',
displayName: 'Blocked ?',
type: 'boolean',
isRequired: false,
},
{
name: 'iban',
displayName: 'IBAN',
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,110 @@
import { EntityProp } from '../types';
export const contactsEntityProps: EntityProp[] = [
{
name: 'number',
displayName: 'Number',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
type: 'text',
isRequired: false,
},
{
name: 'type',
displayName: 'Type',
type: 'static_select',
isRequired: false,
options: [
{
label: 'Person',
value: 'Person',
},
{
label: 'Company',
value: 'Company',
},
],
},
{
name: 'jobTitle',
displayName: 'Job Title',
type: 'text',
isRequired: false,
},
{
name: 'addressLine1',
displayName: 'Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'addressLine2',
displayName: 'Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'city',
displayName: 'City',
type: 'text',
isRequired: false,
},
{
name: 'state',
displayName: 'State',
type: 'text',
isRequired: false,
},
{
name: 'country',
displayName: 'Country',
type: 'text',
isRequired: false,
},
{
name: 'postalCode',
displayName: 'Postal Code',
type: 'text',
isRequired: false,
},
{
name: 'phoneNumber',
displayName: 'Phone Number',
type: 'text',
isRequired: false,
},
{
name: 'mobilePhoneNumber',
displayName: 'Mobile Number',
type: 'text',
isRequired: false,
},
{
name: 'email',
displayName: 'Email',
type: 'text',
isRequired: false,
},
{
name: 'website',
displayName: 'Website',
type: 'text',
isRequired: false,
},
{
name: 'privacyBlocked',
displayName: 'Privacy Blocked?',
type: 'boolean',
isRequired: false,
},
{
name: 'taxRegistrationNumber',
displayName: 'Tax Registration Number',
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,32 @@
import { EntityProp } from '../types';
export const currenciesEntityProps: EntityProp[] = [
{
name: 'displayName',
displayName: 'Display Name',
type: 'text',
isRequired: false,
},
{
name: 'code',
displayName: 'Code',
type: 'text',
isRequired: false,
},
{
name: 'amountDecimalPlaces',
displayName: 'Amount Decimal Places',
description:
'Specifies the number of decimal places the system will display on amounts for this currency.',
type: 'text',
isRequired: false,
},
{
name: 'amountRoundingPrecision',
displayName: 'Amount Rounding Precision',
type: 'number',
isRequired: false,
},
];
export const currenciesEntityNumberProps = ['amountRoundingPrecision'];

View File

@@ -0,0 +1,126 @@
import { EntityProp } from '../types';
export const customersEntityProps: EntityProp[] = [
{
name: 'number',
displayName: 'Number',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
type: 'text',
isRequired: false,
},
{
name: 'type',
displayName: 'Type',
type: 'static_select',
isRequired: false,
options: [
{
label: 'Person',
value: 'Person',
},
{
label: 'Company',
value: 'Company',
},
],
},
{
name: 'addressLine1',
displayName: 'Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'addressLine2',
displayName: 'Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'city',
displayName: 'City',
type: 'text',
isRequired: false,
},
{
name: 'state',
displayName: 'State',
type: 'text',
isRequired: false,
},
{
name: 'country',
displayName: 'Country',
type: 'text',
isRequired: false,
},
{
name: 'email',
displayName: 'Email',
type: 'text',
isRequired: false,
},
{
name: 'website',
displayName: 'Website',
type: 'text',
isRequired: false,
},
{
name: 'taxLiable',
displayName: 'Tax Liable?',
type: 'boolean',
isRequired: false,
},
{
name: 'currencyId',
displayName: 'Currency ID',
type: 'dynamic_select',
isRequired: false,
options: {
labelField: 'code',
sourceFieldSlug: 'currencies',
},
},
{
name: 'currencyCode',
displayName: 'Currency Code',
type: 'text',
isRequired: false,
},
{
name: 'paymentTermsId',
displayName: 'Payment Terms ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'paymentTerms',
labelField: 'code',
},
},
{
name: 'shipmentMethodId',
displayName: 'Shipment Method ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'shipmentMethods',
labelField: 'code',
},
},
{
name: 'paymentMethodId',
displayName: 'Payment Method ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'paymentMethods',
labelField: 'code',
},
},
];

View File

@@ -0,0 +1,18 @@
import { EntityProp } from '../types';
export const disputeStatusEntityProps: EntityProp[] = [
{
name: 'code',
displayName: 'Code',
description: 'The code of the dispute status.',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
description: `Specifies the dispute status's name. This name will appear on all sales documents for the dispute status.`,
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,127 @@
import { EntityProp } from '../types';
export const employeesEntityProps: EntityProp[] = [
{
name: 'number',
displayName: 'Number',
type: 'text',
isRequired: false,
},
{
name: 'givenName',
displayName: 'First Name',
type: 'text',
isRequired: false,
},
{
name: 'middleName',
displayName: 'Middle Name',
type: 'text',
isRequired: false,
},
{
name: 'surname',
displayName: 'Last Name',
type: 'text',
isRequired: false,
},
{
name: 'jobTitle',
displayName: 'Job Title',
type: 'text',
isRequired: false,
},
{
name: 'addressLine1',
displayName: 'Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'addressLine2',
displayName: 'Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'city',
displayName: 'City',
type: 'text',
isRequired: false,
},
{
name: 'state',
displayName: 'State',
type: 'text',
isRequired: false,
},
{
name: 'country',
displayName: 'Country',
type: 'text',
isRequired: false,
},
{
name: 'postalCode',
displayName: 'Postal Code',
type: 'text',
isRequired: false,
},
{
name: 'phoneNumber',
displayName: 'Phone Number',
type: 'text',
isRequired: false,
},
{
name: 'mobilePhone',
displayName: 'Mobile Number',
type: 'text',
isRequired: false,
},
{
name: 'email',
displayName: 'Email',
type: 'text',
isRequired: false,
},
{
name: 'personalEmail',
displayName: 'Personal Email',
type: 'text',
isRequired: false,
},
{
name: 'status',
displayName: 'Status',
type: 'static_select',
description: 'Specifies the status of the employee.',
isRequired: false,
options: [
{
label: 'Active',
value: 'Active',
},
{
label: 'Inactive',
value: 'Inactive',
},
{
label: 'Terminated',
value: 'Terminated',
},
],
},
{
name: 'birthDate',
displayName: 'Birth Date',
type: 'date',
isRequired: false,
},
{
name: 'employmentDate',
displayName: 'Employment Date',
type: 'date',
isRequired: false,
},
];

View File

@@ -0,0 +1,20 @@
import { EntityProp } from '../types';
export const itemCategoriesEntityProps: EntityProp[] = [
{
name: 'code',
displayName: 'Code',
description: 'The code of the item category.',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
description:
"Specifies the item category's name. This name will appear on all sales documents for the item category.",
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,28 @@
import { EntityProp } from '../types';
export const itemVariantsEntityProps: EntityProp[] = [
{
name: 'itemId',
displayName: 'Item ID',
isRequired: false,
type: 'dynamic_select',
options: {
sourceFieldSlug: 'items',
labelField: 'number',
},
},
{
name: 'code',
displayName: 'Code',
description: 'The code of the item variant.',
type: 'text',
isRequired: false,
},
{
name: 'description',
displayName: 'Description',
description: 'Specifies the description of the item variant.',
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,109 @@
import { EntityProp } from '../types';
export const itemsEntityProps: EntityProp[] = [
{
name: 'number',
displayName: 'Number',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
type: 'text',
isRequired: false,
},
{
name: 'type',
displayName: 'Type',
type: 'static_select',
isRequired: false,
options: [
{ label: 'Inventory', value: 'Inventory' },
{ label: 'Service', value: 'Service' },
{ label: 'Non-Inventory', value: 'Non-Inventory' },
],
},
{
name: 'blocked',
displayName: 'Blocked?',
type: 'boolean',
isRequired: false,
},
{
name: 'itemCategoryId',
displayName: 'Item Category ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'itemCategories',
labelField: 'code',
},
},
{
name: 'itemCategoryCode',
displayName: 'Item Category Code',
type: 'text',
isRequired: false,
},
{
name: 'gtin',
displayName: 'GTIN',
type: 'text',
isRequired: false,
},
{
name: 'unitPrice',
displayName: 'Unit Price',
type: 'number',
isRequired: false,
},
{
name: 'priceIncludesTax',
displayName: 'Price Includes Tax?',
description:
'Specifies that the unitPrice includes tax. Set to true, if unitPrice includes tax.',
type: 'boolean',
isRequired: false,
},
{
name: 'unitCost',
displayName: 'Unit Cost',
type: 'number',
isRequired: false,
},
{
name: 'baseUnitOfMeasureId',
displayName: 'Basic Unit of Measure ID',
isRequired: false,
type: 'dynamic_select',
options: {
sourceFieldSlug: 'unitsOfMeasure',
labelField: 'code',
},
},
{
name: 'baseUnitOfMeasureCode',
displayName: 'Basic Unit of Measure Code',
isRequired: false,
type: 'text',
},
{
name: 'taxGroupId',
displayName: 'Tax Group ID',
isRequired: false,
type: 'dynamic_select',
options: {
sourceFieldSlug: 'taxGroups',
labelField: 'code',
},
},
{
name: 'taxGroupCode',
displayName: 'Tax Group Code',
isRequired: false,
type: 'text',
},
];
export const itemsEntityNumberProps = ['unitPrice', 'unitCost'];

View File

@@ -0,0 +1,25 @@
import { EntityProp } from '../types';
export const journalsEntityProps: EntityProp[] = [
{
name: 'code',
displayName: 'Code',
description: 'The code of the journal.',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
description:
"Specifies the journal's name. This name will appear on all sales documents for the journal.",
type: 'text',
isRequired: false,
},
{
name: 'balancingAccountNumber',
displayName: 'Balancing Account Number',
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,72 @@
import { EntityProp } from '../types';
export const locationsEntityProps: EntityProp[] = [
{
name: 'code',
displayName: 'Code',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
description:
"Specifies the location's name. This name will appear on all sales documents for the location.",
type: 'text',
isRequired: false,
},
{
name: 'addressLine1',
displayName: 'Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'addressLine2',
displayName: 'Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'city',
displayName: 'City',
type: 'text',
isRequired: false,
},
{
name: 'state',
displayName: 'State',
type: 'text',
isRequired: false,
},
{
name: 'country',
displayName: 'Country',
type: 'text',
isRequired: false,
},
{
name: 'postalCode',
displayName: 'Postal Code',
type: 'text',
isRequired: false,
},
{
name: 'phoneNumber',
displayName: 'Phone Number',
type: 'text',
isRequired: false,
},
{
name: 'email',
displayName: 'Email',
type: 'text',
isRequired: false,
},
{
name: 'website',
displayName: 'Website',
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,20 @@
import { EntityProp } from '../types';
export const paymentMethodsEntityProps: EntityProp[] = [
{
name: 'code',
displayName: 'Code',
description: 'The code of the payment method.',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
description:
"Specifies the payment method's name. This name will appear on all sales documents for the payment method.",
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,51 @@
import { EntityProp } from '../types';
export const paymentTermsEntityProps: EntityProp[] = [
{
name: 'code',
displayName: 'Code',
description: 'The code of the payment term.',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
description:
"Specifies the payment term's name. This name will appear on all sales documents for the payment term.",
type: 'text',
isRequired: false,
},
{
name: 'dueDateCalculation',
displayName: 'Due Date Calculation',
description:
'Specifies the formula that is used to calculate the date that a payment must be made.',
type: 'text',
isRequired: false,
},
{
name: 'discountDateCalculation',
displayName: 'Discount Date Calculation',
description:
'Specifies the formula that is used to calculate the date that a payment must be made in order to obtain a discount.',
type: 'text',
isRequired: false,
},
{
name: 'discountPercent',
displayName: 'Discount Percent',
type: 'number',
isRequired: false,
},
{
name: 'calculateDiscountOnCreditMemos',
displayName: 'Calc. Pmt. Disc. on Credit Memos',
type: 'boolean',
description:
'Specifies if the discount should be applied to payment term. True indicates a discount will be given, false indicates a discount will not be given.',
isRequired: false,
},
];
export const paymentTermsEntityNumberProps = ['discountPercent'];

View File

@@ -0,0 +1,20 @@
import { EntityProp } from '../types';
export const projectsEntityProps: EntityProp[] = [
{
name: 'number',
displayName: 'Number',
description: 'Specifies the number of the project.',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display name',
description:
"Specifies the project's name. This name will appear on all sales documents for the project.",
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,141 @@
import { EntityProp } from '../types';
//https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/resources/dynamics_salesinvoiceline
export const salesInvoiceLinesEntityProps: EntityProp[] = [
{
name: 'salesInvoiceId',
displayName: 'Sales Invoice ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'salesInvoices',
labelField: 'number',
},
},
{
name: 'lineType',
displayName: 'Type',
type: 'static_select',
isRequired: false,
options: [
// {
// label: 'Comment',
// value: 'Comment',
// },
// {
// label: 'Account',
// value: 'Account',
// },
{
label: 'Item',
value: 'Item',
},
// {
// label: 'Resource',
// value: 'Resource',
// },
// {
// label: 'Value',
// value: 'Value',
// },
// {
// label: 'Charge',
// value: 'Charge',
// },
// {
// label: 'Fixed Asset',
// value: 'Fixed Asset',
// },
],
},
{
name: 'itemId',
displayName: 'Item ID',
isRequired: false,
type: 'dynamic_select',
options: {
sourceFieldSlug: 'items',
labelField: 'number',
},
},
{
name: 'sequence',
displayName: 'Sequence Number',
isRequired: false,
type: 'text',
},
{
name: 'lineObjectNumber',
displayName: 'Line Object Number',
description:
'The number of the object (account or item) of the sales invoice line.',
type: 'text',
isRequired: false,
},
{
name: 'description',
displayName: 'Description',
type: 'text',
isRequired: false,
},
{
name: 'unitOfMeasureId',
displayName: 'Unit of Measure ID',
isRequired: false,
type: 'dynamic_select',
options: {
sourceFieldSlug: 'unitsOfMeasure',
labelField: 'code',
},
},
{
name: 'unitOfMeasureCode',
displayName: 'Unit of Measure Code',
isRequired: false,
type: 'text',
},
{
name: 'quantity',
displayName: 'Quantity',
isRequired: false,
type: 'number',
},
{
name: 'unitPrice',
displayName: 'Unit Price',
isRequired: false,
type: 'number',
},
{
name: 'discountAmount',
displayName: 'Discount Amount',
type: 'number',
isRequired: false,
},
{
name: 'discountPercent',
displayName: 'Discount Percent',
type: 'number',
isRequired: false,
},
{
name: 'taxCode',
displayName: 'Tax Code',
type: 'text',
isRequired: false,
},
{
name: 'shipmentDate',
displayName: 'Shipment Date',
isRequired: false,
type: 'date',
},
];
export const salesInvoiceLinesEntityNumberProps = [
'quantity',
'unitPrice',
'discountPercent',
'discountAmount',
];

View File

@@ -0,0 +1,198 @@
import { EntityProp } from '../types';
// https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/resources/dynamics_salesinvoice
export const salesInvoicesEntityProps: EntityProp[] = [
{
name: 'invoiceDate',
displayName: 'Invoice Date',
type: 'date',
isRequired: false,
},
{
name: 'postingDate',
displayName: 'Posting Date',
type: 'date',
isRequired: false,
},
{
name: 'dueDate',
displayName: 'Due Date',
type: 'date',
isRequired: false,
},
{
name: 'customerId',
displayName: 'Customer ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'customers',
labelField: 'displayName',
},
},
{
name: 'customerNumber',
displayName: 'Customer Number',
type: 'text',
isRequired: false,
},
{
name: 'billToCustomerId',
displayName: 'Bill to Customer ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'customers',
labelField: 'displayName',
},
},
{
name: 'billToCustomerNumber',
displayName: 'Bill to Customer Number',
type: 'text',
isRequired: false,
},
{
name: 'shipToName',
displayName: 'Ship to Name',
type: 'text',
isRequired: false,
},
{
name: 'shipToContact',
displayName: 'Ship to Contact',
type: 'text',
isRequired: false,
},
{
name: 'shipToAddressLine1',
displayName: 'Ship to Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'shipToAddressLine2',
displayName: 'Ship to Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'shipToCity',
displayName: 'Ship to City',
type: 'text',
isRequired: false,
},
{
name: 'shipToCountry',
displayName: 'Ship to Country',
type: 'text',
isRequired: false,
},
{
name: 'shipToState',
displayName: 'Ship to State',
type: 'text',
isRequired: false,
},
{
name: 'shipToPostCode',
displayName: 'Ship to Postalcode',
type: 'text',
isRequired: false,
},
{
name: 'sellToAddressLine1',
displayName: 'Sell to Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'sellToAddressLine2',
displayName: 'Sell to Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'sellToCity',
displayName: 'Sell to City',
type: 'text',
isRequired: false,
},
{
name: 'sellToCountry',
displayName: 'Sell to Country',
type: 'text',
isRequired: false,
},
{
name: 'sellToState',
displayName: 'Sell to State',
type: 'text',
isRequired: false,
},
{
name: 'sellToPostCode',
displayName: 'Sell to Postalcode',
type: 'text',
isRequired: false,
},
{
name: 'currencyId',
displayName: 'Currency ID',
type: 'dynamic_select',
isRequired: false,
options: {
labelField: 'code',
sourceFieldSlug: 'currencies',
},
},
{
name: 'currencyCode',
displayName: 'Currency Code',
type: 'text',
isRequired: false,
},
{
name: 'paymentTermsId',
displayName: 'Payment Terms ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'paymentTerms',
labelField: 'code',
},
},
{
name: 'shipmentMethodId',
displayName: 'Shipment Method ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'shipmentMethods',
labelField: 'code',
},
},
{
name: 'salesperson',
displayName: 'Sales Person Code',
description: 'The salesperson code for the sales invoice.',
isRequired: false,
type: 'text',
},
{
name: 'phoneNumber',
displayName: 'Phone Number',
description: "Specifies the sales invoice's telephone number.",
type: 'text',
isRequired: false,
},
{
name: 'email',
displayName: 'Email',
description: "Specifies the sales invoice's email address.",
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,158 @@
import { EntityProp } from '../types';
// https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/resources/dynamics_salesorderline
export const salesOrdersLinesEntityProps: EntityProp[] = [
{
name: 'salesOrderId',
displayName: 'Sales Order ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'salesOrders',
labelField: 'number',
},
},
{
name: 'lineType',
displayName: 'Type',
type: 'static_select',
isRequired: false,
options: [
// {
// label: 'Comment',
// value: 'Comment',
// },
// {
// label: 'Account',
// value: 'Account',
// },
{
label: 'Item',
value: 'Item',
},
// {
// label: 'Resource',
// value: 'Resource',
// },
// {
// label: 'Value',
// value: 'Value',
// },
// {
// label: 'Charge',
// value: 'Charge',
// },
// {
// label: 'Fixed Asset',
// value: 'Fixed Asset',
// },
],
},
{
name: 'itemId',
displayName: 'Item ID',
isRequired: false,
type: 'dynamic_select',
options: {
sourceFieldSlug: 'items',
labelField: 'number',
},
},
{
name: 'sequence',
displayName: 'Sequence Number',
isRequired: false,
type: 'text',
},
{
name: 'lineObjectNumber',
displayName: 'Line Object Number',
description:
'The number of the object (account or item) of the sales order line.',
type: 'text',
isRequired: false,
},
{
name: 'description',
displayName: 'Description',
type: 'text',
isRequired: false,
},
{
name: 'unitOfMeasureId',
displayName: 'Unit of Measure ID',
isRequired: false,
type: 'dynamic_select',
options: {
sourceFieldSlug: 'unitsOfMeasure',
labelField: 'code',
},
},
{
name: 'unitOfMeasureCode',
displayName: 'Unit of Measure Code',
isRequired: false,
type: 'text',
},
{
name: 'quantity',
displayName: 'Quantity',
isRequired: false,
type: 'number',
},
{
name: 'unitPrice',
displayName: 'Unit Price',
isRequired: false,
type: 'number',
},
{
name: 'discountAmount',
displayName: 'Discount Amount',
type: 'number',
isRequired: false,
},
{
name: 'discountPercent',
displayName: 'Discount Percent',
type: 'number',
isRequired: false,
},
{
name: 'taxCode',
displayName: 'Tax Code',
type: 'text',
isRequired: false,
},
{
name: 'shipmentDate',
displayName: 'Shipment Date',
isRequired: false,
type: 'date',
},
{
name: 'invoiceQuantity',
displayName: 'Invoice Quantity',
type: 'number',
isRequired: false,
description:
'The quantity of items from the sales order line to be invoiced.',
},
{
name: 'shipQuantity',
displayName: 'Ship Quantity',
type: 'number',
isRequired: false,
description: 'The quantity of items from the order to be shipped.',
},
];
export const salesOrderLinesEntityNumberProps = [
'quantity',
'unitPrice',
'discountPercent',
'discountAmount',
'invoicedQuantity',
'shipQuantity',
];

View File

@@ -0,0 +1,218 @@
import { EntityProp } from '../types';
export const salesOrdersEntityProps: EntityProp[] = [
{
name: 'orderDate',
displayName: 'Order Date',
type: 'date',
isRequired: false,
},
{
name: 'postingDate',
displayName: 'Posting Date',
type: 'date',
isRequired: false,
},
{
name: 'customerId',
displayName: 'Customer ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'customers',
labelField: 'displayName',
},
},
{
name: 'customerNumber',
displayName: 'Customer Number',
type: 'text',
isRequired: false,
},
{
name: 'billToCustomerId',
displayName: 'Bill to Customer ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'customers',
labelField: 'displayName',
},
},
{
name: 'billToCustomerNumber',
displayName: 'Bill to Customer Number',
type: 'text',
isRequired: false,
},
{
name: 'shipToName',
displayName: 'Ship to Name',
type: 'text',
isRequired: false,
},
{
name: 'shipToContact',
displayName: 'Ship to Contact',
type: 'text',
isRequired: false,
},
{
name: 'shipToAddressLine1',
displayName: 'Ship to Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'shipToAddressLine2',
displayName: 'Ship to Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'shipToCity',
displayName: 'Ship to City',
type: 'text',
isRequired: false,
},
{
name: 'shipToCountry',
displayName: 'Ship to Country',
type: 'text',
isRequired: false,
},
{
name: 'shipToState',
displayName: 'Ship to State',
type: 'text',
isRequired: false,
},
{
name: 'shipToPostCode',
displayName: 'Ship to Postalcode',
type: 'text',
isRequired: false,
},
{
name: 'sellToAddressLine1',
displayName: 'Sell to Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'sellToAddressLine2',
displayName: 'Sell to Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'sellToCity',
displayName: 'Sell to City',
type: 'text',
isRequired: false,
},
{
name: 'sellToCountry',
displayName: 'Sell to Country',
type: 'text',
isRequired: false,
},
{
name: 'sellToState',
displayName: 'Sell to State',
type: 'text',
isRequired: false,
},
{
name: 'sellToPostCode',
displayName: 'Sell to Postalcode',
type: 'text',
isRequired: false,
},
{
name: 'currencyId',
displayName: 'Currency ID',
type: 'dynamic_select',
isRequired: false,
options: {
labelField: 'code',
sourceFieldSlug: 'currencies',
},
},
{
name: 'currencyCode',
displayName: 'Currency Code',
type: 'text',
isRequired: false,
},
{
name: 'pricesIncludeTax',
displayName: 'Prices include Tax?',
isRequired: false,
type: 'boolean',
},
{
name: 'paymentTermsId',
displayName: 'Payment Terms ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'paymentTerms',
labelField: 'code',
},
},
{
name: 'shipmentMethodId',
displayName: 'Shipment Method ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'shipmentMethods',
labelField: 'code',
},
},
{
name: 'salesperson',
displayName: 'Sales Person Code',
description: 'The salesperson code for the sales order.',
isRequired: false,
type: 'text',
},
{
name: 'partialShipping',
displayName:
'Specifies whether partial shipping of items is preferred or not.',
type: 'boolean',
isRequired: false,
},
{
name: 'requestedDeliveryDate',
displayName: 'Requested Delivery Date',
type: 'date',
isRequired: false,
},
{
name: 'phoneNumber',
displayName: 'Phone Number',
description: "Specifies the sales order's telephone number.",
type: 'text',
isRequired: false,
},
{
name: 'email',
displayName: 'Email',
description: "Specifies the sales order's email address.",
type: 'text',
isRequired: false,
},
{
name: 'fullyShipped',
displayName: 'Fully Shipped?',
description:
'Specifies whether the items of the sales order were fully shipped or not.',
type: 'boolean',
isRequired: false,
},
];
export const salesOrderLinesEntityNumberProps = ['discountAmount'];

View File

@@ -0,0 +1,134 @@
import { EntityProp } from '../types';
//https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/resources/dynamics_salesquoteline
export const salesQuoteLinesEntityProps: EntityProp[] = [
{
name: 'salesQuoteId',
displayName: 'Sales Quote ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'salesQuotes',
labelField: 'number',
},
},
{
name: 'lineType',
displayName: 'Type',
type: 'static_select',
isRequired: false,
options: [
// {
// label: 'Comment',
// value: 'Comment',
// },
// {
// label: 'Account',
// value: 'Account',
// },
{
label: 'Item',
value: 'Item',
},
// {
// label: 'Resource',
// value: 'Resource',
// },
// {
// label: 'Value',
// value: 'Value',
// },
// {
// label: 'Charge',
// value: 'Charge',
// },
// {
// label: 'Fixed Asset',
// value: 'Fixed Asset',
// },
],
},
{
name: 'itemId',
displayName: 'Item ID',
isRequired: false,
type: 'dynamic_select',
options: {
sourceFieldSlug: 'items',
labelField: 'number',
},
},
{
name: 'sequence',
displayName: 'Sequence Number',
isRequired: false,
type: 'text',
},
{
name: 'lineObjectNumber',
displayName: 'Line Object Number',
description:
'The number of the object (account or item) of the sales quote line.',
type: 'text',
isRequired: false,
},
{
name: 'description',
displayName: 'Description',
type: 'text',
isRequired: false,
},
{
name: 'unitOfMeasureId',
displayName: 'Unit of Measure ID',
isRequired: false,
type: 'dynamic_select',
options: {
sourceFieldSlug: 'unitsOfMeasure',
labelField: 'code',
},
},
{
name: 'unitOfMeasureCode',
displayName: 'Unit of Measure Code',
isRequired: false,
type: 'text',
},
{
name: 'quantity',
displayName: 'Quantity',
isRequired: false,
type: 'number',
},
{
name: 'unitPrice',
displayName: 'Unit Price',
isRequired: false,
type: 'number',
},
{
name: 'discountAmount',
displayName: 'Discount Amount',
type: 'number',
isRequired: false,
},
{
name: 'discountPercent',
displayName: 'Discount Percent',
type: 'number',
isRequired: false,
},
{
name: 'taxCode',
displayName: 'Tax Code',
type: 'text',
isRequired: false,
},
];
export const salesQuoteLinesEntityNumberProps = [
'quantity',
'unitPrice',
'discountPercent',
'discountAmount',
];

View File

@@ -0,0 +1,203 @@
import { EntityProp } from '../types';
// https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/api/dynamics_salesquote_create
export const salesQuotesEntityProps: EntityProp[] = [
{
name: 'number',
displayName: 'Number',
type: 'text',
isRequired: false,
},
{
name: 'documentDate',
displayName: 'Quote Date',
type: 'date',
isRequired: false,
},
{
name: 'postingDate',
displayName: 'Posting Date',
type: 'date',
isRequired: false,
},
{
name: 'customerId',
displayName: 'Customer ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'customers',
labelField: 'displayName',
},
},
{
name: 'customerNumber',
displayName: 'Customer Number',
type: 'text',
isRequired: false,
},
{
name: 'billToCustomerId',
displayName: 'Bill to Customer ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'customers',
labelField: 'displayName',
},
},
{
name: 'billToCustomerNumber',
displayName: 'Bill to Customer Number',
type: 'text',
isRequired: false,
},
{
name: 'shipToName',
displayName: 'Ship to Name',
type: 'text',
isRequired: false,
},
{
name: 'shipToContact',
displayName: 'Ship to Contact',
type: 'text',
isRequired: false,
},
{
name: 'shipToAddressLine1',
displayName: 'Ship to Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'shipToAddressLine2',
displayName: 'Ship to Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'shipToCity',
displayName: 'Ship to City',
type: 'text',
isRequired: false,
},
{
name: 'shipToCountry',
displayName: 'Ship to Country',
type: 'text',
isRequired: false,
},
{
name: 'shipToState',
displayName: 'Ship to State',
type: 'text',
isRequired: false,
},
{
name: 'shipToPostCode',
displayName: 'Ship to Postalcode',
type: 'text',
isRequired: false,
},
{
name: 'sellToAddressLine1',
displayName: 'Sell to Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'sellToAddressLine2',
displayName: 'Sell to Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'sellToCity',
displayName: 'Sell to City',
type: 'text',
isRequired: false,
},
{
name: 'sellToCountry',
displayName: 'Sell to Country',
type: 'text',
isRequired: false,
},
{
name: 'sellToState',
displayName: 'Sell to State',
type: 'text',
isRequired: false,
},
{
name: 'sellToPostCode',
displayName: 'Sell to Postalcode',
type: 'text',
isRequired: false,
},
{
name: 'currencyId',
displayName: 'Currency ID',
type: 'dynamic_select',
isRequired: false,
options: {
labelField: 'code',
sourceFieldSlug: 'currencies',
},
},
{
name: 'currencyCode',
displayName: 'Currency Code',
type: 'text',
isRequired: false,
},
{
name: 'paymentTermsId',
displayName: 'Payment Terms ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'paymentTerms',
labelField: 'code',
},
},
{
name: 'shipmentMethodId',
displayName: 'Shipment Method ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'shipmentMethods',
labelField: 'code',
},
},
{
name: 'salesperson',
displayName: 'Sales Person Code',
description: 'The salesperson code for the sales quote.',
isRequired: false,
type: 'text',
},
{
name: 'phoneNumber',
displayName: 'Phone Number',
description: "Specifies the sales quote's telephone number.",
type: 'text',
isRequired: false,
},
{
name: 'email',
displayName: 'Email',
description: "Specifies the sales quote's email address.",
type: 'text',
isRequired: false,
},
{
name: 'validUntilDate',
displayName: 'Valid Until Date',
isRequired: false,
type: 'date',
},
];

View File

@@ -0,0 +1,19 @@
import { EntityProp } from '../types';
export const shipmentMethodsEntityProps: EntityProp[] = [
{
name: 'displayName',
displayName: 'Display Name',
description:
"Specifies the shipment method's name. This name will appear on all sales documents for the shipment method.",
type: 'text',
isRequired: false,
},
{
name: 'code',
displayName: 'Code',
description: 'TThe code of the shipment method.',
type: 'text',
isRequired: false,
},
];

View File

@@ -0,0 +1,112 @@
import { EntityProp } from '../types';
export const vendorsEntityProps: EntityProp[] = [
{
name: 'number',
displayName: 'Number',
type: 'text',
isRequired: false,
},
{
name: 'displayName',
displayName: 'Display Name',
type: 'text',
isRequired: false,
},
{
name: 'addressLine1',
displayName: 'Address Line 1',
type: 'text',
isRequired: false,
},
{
name: 'addressLine2',
displayName: 'Address Line 2',
type: 'text',
isRequired: false,
},
{
name: 'city',
displayName: 'City',
type: 'text',
isRequired: false,
},
{
name: 'state',
displayName: 'State',
type: 'text',
isRequired: false,
},
{
name: 'country',
displayName: 'Country',
type: 'text',
isRequired: false,
},
{
name: 'postalCode',
displayName: 'Postal Code',
type: 'text',
isRequired: false,
},
{
name: 'email',
displayName: 'Email',
type: 'text',
isRequired: false,
},
{
name: 'website',
displayName: 'Website',
type: 'text',
isRequired: false,
},
{
name: 'taxLiable',
displayName: 'Tax Liable?',
type: 'boolean',
isRequired: false,
},
{
name: 'taxRegistrationNumber',
displayName: 'Tax Registration Number',
type: 'text',
isRequired: false,
},
{
name: 'currencyId',
displayName: 'Currency ID',
type: 'dynamic_select',
isRequired: false,
options: {
labelField: 'code',
sourceFieldSlug: 'currencies',
},
},
{
name: 'currencyCode',
displayName: 'Currency Code',
type: 'text',
isRequired: false,
},
{
name: 'paymentTermsId',
displayName: 'Payment Terms ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'paymentTerms',
labelField: 'code',
},
},
{
name: 'paymentMethodId',
displayName: 'Payment Method ID',
type: 'dynamic_select',
isRequired: false,
options: {
sourceFieldSlug: 'paymentMethods',
labelField: 'code',
},
},
];

View File

@@ -0,0 +1,62 @@
interface BaseField {
name: string;
displayName: string;
description?: string;
isRequired: boolean;
}
interface TextField extends BaseField {
type: 'text';
}
interface MultiTextField extends BaseField {
type: 'multi_text';
}
interface NumberField extends BaseField {
type: 'number';
}
interface BooleanField extends BaseField {
type: 'boolean';
}
interface DateField extends BaseField {
type: 'date';
}
interface StaticSelectField extends BaseField {
type: 'static_select';
options: Array<{ label: string; value: string }>;
}
interface StaticMultiSelectField extends BaseField {
type: 'static_multi_select';
options: Array<{ label: string; value: string }>;
}
interface DynamicSingleSelectField extends BaseField {
type: 'dynamic_select';
options: {
sourceFieldSlug: string;
labelField: string;
};
}
interface DynamicMultiSelectField extends BaseField {
type: 'dynamic_multi_select';
options: {
sourceFieldSlug: string;
labelField: string;
};
}
export type EntityProp =
| TextField
| MultiTextField
| NumberField
| BooleanField
| DateField
| StaticMultiSelectField
| StaticSelectField
| DynamicMultiSelectField
| DynamicSingleSelectField;

View File

@@ -0,0 +1,98 @@
import { businessCentralAuth } from '../../';
import {
DedupeStrategy,
Polling,
pollingHelper,
} from '@activepieces/pieces-common';
import {
createTrigger,
AppConnectionValueForAuthProperty,
Property,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { commonProps } from '../common';
import { filterParams, makeClient } from '../common/client';
import dayjs from 'dayjs';
import { TRIGGER_ENTITY_DROPDOWN_OPTIONS } from '../common/constants';
const polling: Polling<
AppConnectionValueForAuthProperty<typeof businessCentralAuth>,
{ company_id: string; record_type: string }
> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, propsValue, lastFetchEpochMS }) => {
const filter: filterParams = {};
if (lastFetchEpochMS) {
filter['$filter'] = `lastModifiedDateTime gt ${dayjs(
lastFetchEpochMS
).toISOString()}`;
} else {
filter['$top'] = 10;
}
const client = makeClient(auth);
const response = await client.filterRecords(
propsValue.company_id,
propsValue.record_type,
filter
);
return response.value.map((item: any) => {
return {
epochMilliSeconds: dayjs(item['lastModifiedDateTime']).valueOf(),
data: item,
};
});
},
};
export const newOrUpdatedRecordTrigger = createTrigger({
auth: businessCentralAuth,
name: 'new-or-updated-record',
displayName: 'New or Updated Record',
description: 'Triggers when a new record is added or modified.',
type: TriggerStrategy.POLLING,
sampleData: {},
props: {
company_id: commonProps.company_id,
record_type: Property.StaticDropdown({
displayName: 'Record Type',
required: true,
options: {
disabled: false,
options: TRIGGER_ENTITY_DROPDOWN_OPTIONS,
},
}),
},
async test(ctx) {
return await pollingHelper.test(polling, {
auth: ctx.auth,
store: ctx.store,
propsValue: ctx.propsValue,
files: ctx.files,
});
},
async onEnable(ctx) {
await pollingHelper.onEnable(polling, {
auth: ctx.auth,
store: ctx.store,
propsValue: ctx.propsValue,
});
},
async onDisable(ctx) {
await pollingHelper.onDisable(polling, {
auth: ctx.auth,
store: ctx.store,
propsValue: ctx.propsValue,
});
},
async run(ctx) {
return await pollingHelper.poll(polling, {
auth: ctx.auth,
store: ctx.store,
propsValue: ctx.propsValue,
files: ctx.files,
});
},
});