diff --git a/components/sage_accounting/actions/create-contact-payment/create-contact-payment.mjs b/components/sage_accounting/actions/create-contact-payment/create-contact-payment.mjs new file mode 100644 index 0000000000000..6595b0630f139 --- /dev/null +++ b/components/sage_accounting/actions/create-contact-payment/create-contact-payment.mjs @@ -0,0 +1,139 @@ +import app from "../../sage_accounting.app.mjs"; +import { optionalParseFloat } from "../../common/utils.mjs"; + +export default { + key: "sage_accounting-create-contact-payment", + name: "Create Contact Payment", + description: "Creates a new contact payment in Sage Accounting. [See the documentation](https://developer.sage.com/accounting/reference/payments/#operation/postContactPayments)", + version: "0.0.1", + type: "action", + props: { + app, + transactionTypeId: { + propDefinition: [ + app, + "transactionTypeId", + ], + }, + contactId: { + propDefinition: [ + app, + "contactId", + ], + }, + bankAccountId: { + propDefinition: [ + app, + "bankAccountId", + ], + }, + date: { + propDefinition: [ + app, + "date", + ], + }, + totalAmount: { + propDefinition: [ + app, + "totalAmount", + ], + }, + paymentMethodId: { + propDefinition: [ + app, + "paymentMethodId", + ], + }, + netAmount: { + propDefinition: [ + app, + "netAmount", + ], + }, + taxAmount: { + propDefinition: [ + app, + "taxAmount", + ], + }, + currencyId: { + propDefinition: [ + app, + "currencyId", + ], + }, + exchangeRate: { + propDefinition: [ + app, + "exchangeRate", + ], + }, + baseCurrencyNetAmount: { + propDefinition: [ + app, + "baseCurrencyNetAmount", + ], + }, + baseCurrencyTaxAmount: { + propDefinition: [ + app, + "baseCurrencyTaxAmount", + ], + }, + baseCurrencyTotalAmount: { + propDefinition: [ + app, + "baseCurrencyTotalAmount", + ], + }, + baseCurrencyCurrencyCharge: { + propDefinition: [ + app, + "baseCurrencyCurrencyCharge", + ], + }, + paymentReference: { + propDefinition: [ + app, + "paymentReference", + ], + }, + taxRateId: { + propDefinition: [ + app, + "taxRateId", + ], + }, + }, + async run({ $ }) { + const data = { + contact_payment: { + transaction_type_id: this.transactionTypeId, + contact_id: this.contactId, + bank_account_id: this.bankAccountId, + date: this.date, + total_amount: parseFloat(this.totalAmount), + payment_method_id: this.paymentMethodId, + net_amount: optionalParseFloat(this.netAmount), + tax_amount: optionalParseFloat(this.taxAmount), + currency_id: this.currencyId, + exchange_rate: optionalParseFloat(this.exchangeRate), + base_currency_net_amount: optionalParseFloat(this.baseCurrencyNetAmount), + base_currency_tax_amount: optionalParseFloat(this.baseCurrencyTaxAmount), + base_currency_total_amount: optionalParseFloat(this.baseCurrencyTotalAmount), + base_currency_currency_charge: optionalParseFloat(this.baseCurrencyCurrencyCharge), + reference: this.paymentReference, + tax_rate_id: this.taxRateId, + }, + }; + + const response = await this.app.createContactPayment({ + $, + data, + }); + + $.export("$summary", `Successfully created contact payment (ID: ${response.id})`); + return response; + }, +}; diff --git a/components/sage_accounting/actions/create-contact/create-contact.mjs b/components/sage_accounting/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..89e5e2c61cb4f --- /dev/null +++ b/components/sage_accounting/actions/create-contact/create-contact.mjs @@ -0,0 +1,175 @@ +import app from "../../sage_accounting.app.mjs"; + +export default { + key: "sage_accounting-create-contact", + name: "Create Contact", + description: "Creates a new contact in Sage Accounting. [See the documentation](https://developer.sage.com/accounting/reference/contacts/#tag/Contacts/operation/postContacts)", + version: "0.0.1", + type: "action", + props: { + app, + name: { + propDefinition: [ + app, + "name", + ], + }, + contactTypeIds: { + propDefinition: [ + app, + "contactTypeIds", + ], + }, + reference: { + propDefinition: [ + app, + "reference", + ], + }, + taxNumber: { + propDefinition: [ + app, + "taxNumber", + ], + }, + notes: { + propDefinition: [ + app, + "notes", + ], + }, + locale: { + propDefinition: [ + app, + "locale", + ], + }, + creditLimit: { + propDefinition: [ + app, + "creditLimit", + ], + }, + creditDays: { + propDefinition: [ + app, + "creditDays", + ], + }, + creditTerms: { + propDefinition: [ + app, + "creditTerms", + ], + }, + creditTermsAndConditions: { + propDefinition: [ + app, + "creditTermsAndConditions", + ], + }, + productSalesPriceTypeId: { + propDefinition: [ + app, + "productSalesPriceTypeId", + ], + }, + sourceGuid: { + propDefinition: [ + app, + "sourceGuid", + ], + }, + currencyId: { + propDefinition: [ + app, + "currencyId", + ], + }, + auxReference: { + propDefinition: [ + app, + "auxReference", + ], + }, + registeredNumber: { + propDefinition: [ + app, + "registeredNumber", + ], + }, + taxCalculation: { + propDefinition: [ + app, + "taxCalculation", + ], + }, + auxiliaryAccount: { + propDefinition: [ + app, + "auxiliaryAccount", + ], + }, + destinationVatBlocking: { + propDefinition: [ + app, + "destinationVatBlocking", + ], + }, + defaultSalesLedgerAccountId: { + propDefinition: [ + app, + "ledgerAccountId", + ], + label: "Default Sales Ledger Account ID", + description: "The ID of the default sales ledger account for the contact", + }, + defaultSalesTaxRateId: { + propDefinition: [ + app, + "defaultSalesTaxRateId", + ], + }, + defaultPurchaseLedgerAccountId: { + propDefinition: [ + app, + "ledgerAccountId", + ], + label: "Default Purchase Ledger Account ID", + description: "The ID of the default purchase ledger account for the contact", + }, + }, + async run({ $ }) { + const response = await this.app.createContact({ + $, + data: { + contact: { + name: this.name, + contact_type_ids: this.contactTypeIds, + reference: this.reference, + default_sales_ledger_account_id: this.defaultSalesLedgerAccountId, + default_sales_tax_rate_id: this.defaultSalesTaxRateId, + default_purchase_ledger_account_id: this.defaultPurchaseLedgerAccountId, + tax_number: this.taxNumber, + notes: this.notes, + locale: this.locale, + credit_limit: this.creditLimit, + credit_days: this.creditDays, + credit_terms: this.creditTerms, + credit_terms_and_conditions: this.creditTermsAndConditions, + product_sales_price_type_id: this.productSalesPriceTypeId, + source_guid: this.sourceGuid, + currency_id: this.currencyId, + aux_reference: this.auxReference, + registered_number: this.registeredNumber, + tax_calculation: this.taxCalculation, + auxiliary_account: this.auxiliaryAccount, + destination_vat_blocking: this.destinationVatBlocking, + }, + }, + }); + + $.export("$summary", `Successfully created contact: ${response.name || response.displayed_as}`); + return response; + }, +}; diff --git a/components/sage_accounting/actions/create-product/create-product.mjs b/components/sage_accounting/actions/create-product/create-product.mjs new file mode 100644 index 0000000000000..077484f6426c3 --- /dev/null +++ b/components/sage_accounting/actions/create-product/create-product.mjs @@ -0,0 +1,150 @@ +import app from "../../sage_accounting.app.mjs"; + +export default { + key: "sage_accounting-create-product", + name: "Create Product", + description: "Creates a new product in Sage Accounting. [See the documentation](https://developer.sage.com/accounting/reference/products/#tag/Products/operation/postProducts)", + version: "0.0.1", + type: "action", + props: { + app, + description: { + type: "string", + label: "Description", + description: "The product description", + }, + salesLedgerAccountId: { + propDefinition: [ + app, + "ledgerAccountId", + () => ({ + type: "SALES", + }), + ], + label: "Sales Ledger Account ID", + description: "The sales ledger account for the product", + optional: false, + }, + purchaseLedgerAccountId: { + propDefinition: [ + app, + "ledgerAccountId", + () => ({ + type: "DIRECT_EXPENSES", + }), + ], + label: "Purchase Ledger Account ID", + description: "The purchase ledger account for the product", + optional: false, + }, + itemCode: { + type: "string", + label: "Item Code", + description: "The item code for the product", + optional: true, + }, + notes: { + type: "string", + label: "Notes", + description: "The notes for the product", + optional: true, + }, + salesTaxRateId: { + propDefinition: [ + app, + "taxRateId", + ], + label: "Sales Tax Rate ID", + description: "The ID of the Sales Tax Rate", + }, + usualSupplierId: { + type: "string", + label: "Usual Supplier ID", + description: "The ID of the Usual Supplier", + async options({ page }) { + const suppliers = await this.app.listSuppliers({ + page, + }); + return suppliers.map((supplier) => ({ + label: supplier.displayed_as, + value: supplier.id, + })); + }, + optional: true, + }, + purchaseTaxRateId: { + propDefinition: [ + app, + "taxRateId", + ], + label: "Purchase Tax Rate ID", + description: "The ID of the Purchase Tax Rate", + }, + costPrice: { + type: "string", + label: "Cost Price", + description: "The cost price of the product", + optional: true, + }, + sourceGuid: { + type: "string", + label: "Source GUID", + description: "Used when importing products from external sources", + optional: true, + }, + purchaseDescription: { + type: "string", + label: "Purchase Description", + description: "The product purchase description", + optional: true, + }, + active: { + type: "boolean", + label: "Active", + description: "Indicates whether the product is active", + optional: true, + default: true, + }, + catalogItemTypeId: { + type: "string", + label: "Catalog Item Type ID", + description: "The ID of the Catalog Item Type", + async options({ page }) { + const catalogItemTypes = await this.app.listCatalogItemTypes({ + page, + }); + return catalogItemTypes.map((type) => ({ + label: type.displayed_as, + value: type.id, + })); + }, + optional: true, + }, + }, + async run({ $ }) { + + const response = await this.app.createProduct({ + $, + data: { + product: { + description: this.description, + sales_ledger_account_id: this.salesLedgerAccountId, + purchase_ledger_account_id: this.purchaseLedgerAccountId, + item_code: this.itemCode, + notes: this.notes, + sales_tax_rate_id: this.salesTaxRateId, + usual_supplier_id: this.usualSupplierId, + purchase_tax_rate_id: this.purchaseTaxRateId, + cost_price: this.costPrice, + source_guid: this.sourceGuid, + purchase_description: this.purchaseDescription, + active: this.active, + catalog_item_type_id: this.catalogItemTypeId, + }, + }, + }); + + $.export("$summary", `Successfully created product: ${response.description || response.displayed_as}`); + return response; + }, +}; diff --git a/components/sage_accounting/actions/get-ledger-accounts/get-ledger-accounts.mjs b/components/sage_accounting/actions/get-ledger-accounts/get-ledger-accounts.mjs new file mode 100644 index 0000000000000..b4575f83a238f --- /dev/null +++ b/components/sage_accounting/actions/get-ledger-accounts/get-ledger-accounts.mjs @@ -0,0 +1,52 @@ +import app from "../../sage_accounting.app.mjs"; + +export default { + key: "sage_accounting-get-ledger-accounts", + name: "Get Ledger Accounts", + description: "Retrieves a list of ledger accounts from Sage Accounting. [See the documentation](https://developer.sage.com/accounting/reference/accounting-setup/#tag/Ledger-Accounts/operation/getLedgerAccounts)", + version: "0.0.1", + type: "action", + props: { + app, + ledgerAccountType: { + propDefinition: [ + app, + "ledgerAccountType", + ], + }, + itemsPerPage: { + type: "integer", + label: "Max Items", + description: "Maximum number of items to return (1-200)", + min: 1, + max: 200, + optional: true, + default: 20, + }, + page: { + type: "integer", + label: "Page", + description: "Page number to retrieve (starts from 1)", + min: 1, + optional: true, + default: 1, + }, + }, + async run({ $ }) { + const response = await this.app.listLedgerAccounts({ + $, + params: { + ledger_account_type_id: this.ledgerAccountType, + items_per_page: this.itemsPerPage, + page: this.page, + }, + }); + + const length = response.length; + + $.export("$summary", `Successfully retrieved ${length} ledger account${length !== 1 + ? "s" + : ""}`); + return response; + }, +}; diff --git a/components/sage_accounting/actions/update-contact/update-contact.mjs b/components/sage_accounting/actions/update-contact/update-contact.mjs new file mode 100644 index 0000000000000..a57961651326e --- /dev/null +++ b/components/sage_accounting/actions/update-contact/update-contact.mjs @@ -0,0 +1,184 @@ +import app from "../../sage_accounting.app.mjs"; + +export default { + key: "sage_accounting-update-contact", + name: "Update Contact", + description: "Updates an existing contact in Sage Accounting. [See the documentation](https://developer.sage.com/accounting/reference/contacts/#tag/Contacts/operation/putContactsKey)", + version: "0.0.1", + type: "action", + props: { + app, + contactId: { + propDefinition: [ + app, + "contactId", + ], + }, + name: { + propDefinition: [ + app, + "name", + ], + optional: true, + }, + contactTypeIds: { + propDefinition: [ + app, + "contactTypeIds", + ], + optional: true, + }, + reference: { + propDefinition: [ + app, + "reference", + ], + }, + defaultSalesLedgerAccountId: { + propDefinition: [ + app, + "ledgerAccountId", + ], + label: "Default Sales Ledger Account ID", + description: "The ID of the default sales ledger account for the contact", + }, + defaultSalesTaxRateId: { + propDefinition: [ + app, + "defaultSalesTaxRateId", + ], + }, + defaultPurchaseLedgerAccountId: { + propDefinition: [ + app, + "ledgerAccountId", + ], + label: "Default Purchase Ledger Account ID", + description: "The ID of the default purchase ledger account for the contact", + }, + taxNumber: { + propDefinition: [ + app, + "taxNumber", + ], + }, + notes: { + propDefinition: [ + app, + "notes", + ], + }, + locale: { + propDefinition: [ + app, + "locale", + ], + }, + creditLimit: { + propDefinition: [ + app, + "creditLimit", + ], + }, + creditDays: { + propDefinition: [ + app, + "creditDays", + ], + }, + creditTerms: { + propDefinition: [ + app, + "creditTerms", + ], + }, + creditTermsAndConditions: { + propDefinition: [ + app, + "creditTermsAndConditions", + ], + }, + productSalesPriceTypeId: { + propDefinition: [ + app, + "productSalesPriceTypeId", + ], + }, + sourceGuid: { + propDefinition: [ + app, + "sourceGuid", + ], + }, + currencyId: { + propDefinition: [ + app, + "currencyId", + ], + }, + auxReference: { + propDefinition: [ + app, + "auxReference", + ], + }, + registeredNumber: { + propDefinition: [ + app, + "registeredNumber", + ], + }, + taxCalculation: { + propDefinition: [ + app, + "taxCalculation", + ], + }, + auxiliaryAccount: { + propDefinition: [ + app, + "auxiliaryAccount", + ], + }, + destinationVatBlocking: { + propDefinition: [ + app, + "destinationVatBlocking", + ], + }, + }, + async run({ $ }) { + const response = await this.app.updateContact({ + $, + contactId: this.contactId, + data: { + contact: { + name: this.name, + contact_type_ids: this.contactTypeIds, + reference: this.reference, + default_sales_ledger_account_id: this.defaultSalesLedgerAccountId, + default_sales_tax_rate_id: this.defaultSalesTaxRateId, + default_purchase_ledger_account_id: this.defaultPurchaseLedgerAccountId, + tax_number: this.taxNumber, + notes: this.notes, + locale: this.locale, + credit_limit: this.creditLimit, + credit_days: this.creditDays, + credit_terms: this.creditTerms, + credit_terms_and_conditions: this.creditTermsAndConditions, + product_sales_price_type_id: this.productSalesPriceTypeId, + source_guid: this.sourceGuid, + currency_id: this.currencyId, + aux_reference: this.auxReference, + registered_number: this.registeredNumber, + tax_calculation: this.taxCalculation, + auxiliary_account: this.auxiliaryAccount, + destination_vat_blocking: this.destinationVatBlocking, + }, + }, + }); + + $.export("$summary", `Successfully updated contact: ${response.name || response.displayed_as}`); + return response; + }, +}; diff --git a/components/sage_accounting/common/constants.mjs b/components/sage_accounting/common/constants.mjs new file mode 100644 index 0000000000000..efe86e6d9e490 --- /dev/null +++ b/components/sage_accounting/common/constants.mjs @@ -0,0 +1,53 @@ +export const CREDIT_TERMS_OPTIONS = [ + { + label: "Month End Invoice", + value: "month_end_invoice", + }, + { + label: "Date From Invoice", + value: "date_from_invoice", + }, + { + label: "Immediate Invoice", + value: "immediate_invoice", + }, + { + label: "Month End Payment", + value: "month_end_payment", + }, + { + label: "Date From Payment", + value: "date_from_payment", + }, + { + label: "Immediate Payment", + value: "immediate_payment", + }, +]; + +export const LOCALE_OPTIONS = [ + { + label: "English (US)", + value: "en-US", + }, + { + label: "English (UK)", + value: "en-GB", + }, + { + label: "French", + value: "fr-FR", + }, + { + label: "German", + value: "de-DE", + }, + { + label: "Spanish", + value: "es-ES", + }, + { + label: "Italian", + value: "it-IT", + }, +]; diff --git a/components/sage_accounting/common/utils.mjs b/components/sage_accounting/common/utils.mjs new file mode 100644 index 0000000000000..1ae476b915870 --- /dev/null +++ b/components/sage_accounting/common/utils.mjs @@ -0,0 +1,5 @@ +export function optionalParseFloat(value) { + return typeof value === "string" + ? parseFloat(value) + : value; +} diff --git a/components/sage_accounting/package.json b/components/sage_accounting/package.json index 9a21b6e0bd1b2..1b68dc383325f 100644 --- a/components/sage_accounting/package.json +++ b/components/sage_accounting/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/sage_accounting", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Sage Accounting Components", "main": "sage_accounting.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.1.0" } -} \ No newline at end of file +} diff --git a/components/sage_accounting/sage_accounting.app.mjs b/components/sage_accounting/sage_accounting.app.mjs index a8c3c00fc54b1..97002555efe50 100644 --- a/components/sage_accounting/sage_accounting.app.mjs +++ b/components/sage_accounting/sage_accounting.app.mjs @@ -1,11 +1,508 @@ +import { axios } from "@pipedream/platform"; +import { + CREDIT_TERMS_OPTIONS, LOCALE_OPTIONS, +} from "./common/constants.mjs"; + export default { type: "app", app: "sage_accounting", - propDefinitions: {}, + propDefinitions: { + ledgerAccountType: { + type: "string", + label: "Ledger Account Type", + description: "The type of ledger account to retrieve", + optional: true, + async options({ page = 0 }) { + const ledgerAccountTypes = await this.listLedgerAccountTypes({ + params: { + page, + }, + }); + return ledgerAccountTypes.map((ledgerAccountType) => ({ + label: ledgerAccountType.displayed_as, + value: ledgerAccountType.id, + })); + }, + }, + contactId: { + type: "string", + label: "Contact ID", + description: "The unique identifier for the contact", + async options({ page = 0 }) { + const contacts = await this.listContacts({ + params: { + page, + }, + }); + return contacts.map((contact) => ({ + label: contact.name || contact.displayed_as, + value: contact.id, + })); + }, + }, + contactTypeIds: { + type: "string[]", + label: "Contact Type IDs", + description: "The IDs of the Contact Types", + async options({ page = 0 }) { + const contactTypes = await this.listContactTypes({ + params: { + page, + }, + }); + return contactTypes.map((contactType) => ({ + label: contactType.displayed_as, + value: contactType.id, + })); + }, + default: [ + "CUSTOMER", + ], + }, + currencyId: { + type: "string", + label: "Currency ID", + description: "The ID of the Currency", + async options({ page = 0 }) { + const currencies = await this.listCurrencies({ + params: { + page, + }, + }); + return currencies.map((currency) => ({ + label: currency.displayed_as, + value: currency.id, + })); + }, + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "The contact's full name or business name", + }, + reference: { + type: "string", + label: "Reference", + description: "Unique reference for the contact", + optional: true, + }, + ledgerAccountId: { + type: "string", + label: "Ledger Account ID", + description: "The ID of the ledger account", + async options({ + page = 0, type, + }) { + const ledgerAccounts = await this.listLedgerAccounts({ + params: { + page, + ledger_account_type_id: type, + }, + }); + return ledgerAccounts.map((account) => ({ + label: account.displayed_as, + value: account.id, + })); + }, + optional: true, + }, + defaultSalesTaxRateId: { + type: "string", + label: "Default Sales Tax Rate ID", + description: "The ID of the default sales tax rate for the contact", + optional: true, + }, + taxNumber: { + type: "string", + label: "Tax Number", + description: + "The VAT registration number of the contact. The format will be validated.", + optional: true, + }, + notes: { + type: "string", + label: "Notes", + description: "The notes for the contact", + optional: true, + }, + locale: { + type: "string", + label: "Locale", + description: "The locale for the contact", + options: LOCALE_OPTIONS, + optional: true, + }, + creditLimit: { + type: "string", + label: "Credit Limit", + description: + "Custom credit limit amount for the contact (not applicable to Start)", + optional: true, + }, + creditDays: { + type: "integer", + label: "Credit Days", + description: "Custom credit days for the contact (0-365)", + min: 0, + max: 365, + optional: true, + }, + creditTerms: { + type: "string", + label: "Credit Terms", + description: + "Credit terms options determine how invoice due dates are calculated", + options: CREDIT_TERMS_OPTIONS, + optional: true, + }, + creditTermsAndConditions: { + type: "string", + label: "Credit Terms and Conditions", + description: + "Custom terms and conditions for the contact. If set will override global /invoice_settings default terms and conditions. (Customers only)", + optional: true, + }, + productSalesPriceTypeId: { + type: "string", + label: "Product Sales Price Type ID", + description: "The ID of the product sales price type for the contact", + optional: true, + }, + sourceGuid: { + type: "string", + label: "Source GUID", + description: "Used when importing contacts from external sources", + optional: true, + }, + auxReference: { + type: "string", + label: "Auxiliary Reference", + description: + "Auxiliary reference. Used for German 'Kreditorennummer' and 'Debitorennummer'", + optional: true, + }, + registeredNumber: { + type: "string", + label: "Registered Number", + description: + "The registered number of the contact's business. Only used for German businesses and represents the 'Steuernummer' there (not the 'USt-ID')", + optional: true, + }, + taxCalculation: { + type: "string", + label: "Tax Calculation", + description: + "Tax calculation method used to define tax treatment (varies by country)", + optional: true, + }, + auxiliaryAccount: { + type: "string", + label: "Auxiliary Account", + description: + "Auxiliary account - used when auxiliary accounting is enabled in business settings. Available only in Spain and France", + optional: true, + }, + destinationVatBlocking: { + type: "boolean", + label: "Destination VAT Blocking", + description: + "Identifies a contact should be blocked due to destination VAT", + optional: true, + }, + // Contact Payment propDefinitions + transactionTypeId: { + type: "string", + label: "Transaction Type ID", + description: "The transaction type of the payment", + async options({ page = 0 }) { + const transactionTypes = await this.listTransactionTypes({ + params: { + page, + }, + }); + return transactionTypes.map((transactionType) => ({ + label: transactionType.displayed_as, + value: transactionType.id, + })); + }, + }, + bankAccountId: { + type: "string", + label: "Bank Account ID", + description: "The bank account of the payment", + async options({ page = 0 }) { + const bankAccounts = await this.listBankAccounts({ + params: { + page, + }, + }); + return bankAccounts.map((bankAccount) => ({ + label: bankAccount.displayed_as, + value: bankAccount.id, + })); + }, + }, + paymentMethodId: { + type: "string", + label: "Payment Method ID", + description: "The ID of the Payment Method", + async options({ page = 0 }) { + const paymentMethods = await this.listPaymentMethods({ + params: { + page, + }, + }); + return paymentMethods.map((paymentMethod) => ({ + label: paymentMethod.displayed_as, + value: paymentMethod.id, + })); + }, + optional: true, + }, + taxRateId: { + type: "string", + label: "Tax Rate ID", + description: "The ID of the Tax Rate", + async options({ page = 0 }) { + const taxRates = await this.listTaxRates({ + params: { + page, + }, + }); + return taxRates.map((taxRate) => ({ + label: taxRate.displayed_as, + value: taxRate.id, + })); + }, + optional: true, + }, + date: { + type: "string", + label: "Date", + description: "The date the payment was made (YYYY-MM-DD format)", + }, + totalAmount: { + type: "string", + label: "Total Amount", + description: "The total amount of the payment", + }, + netAmount: { + type: "string", + label: "Net Amount", + description: "The net amount of the payment", + optional: true, + }, + taxAmount: { + type: "string", + label: "Tax Amount", + description: "The tax amount of the payment", + optional: true, + }, + exchangeRate: { + type: "string", + label: "Exchange Rate", + description: "The exchange rate of the payment", + optional: true, + }, + baseCurrencyNetAmount: { + type: "string", + label: "Base Currency Net Amount", + description: "The net amount of the payment in base currency", + optional: true, + }, + baseCurrencyTaxAmount: { + type: "string", + label: "Base Currency Tax Amount", + description: "The tax amount of the payment in base currency", + optional: true, + }, + baseCurrencyTotalAmount: { + type: "string", + label: "Base Currency Total Amount", + description: "The total amount of the payment in base currency", + optional: true, + }, + baseCurrencyCurrencyCharge: { + type: "string", + label: "Base Currency Currency Charge", + description: "The currency conversion charges in base currency", + optional: true, + }, + paymentReference: { + type: "string", + label: "Payment Reference", + description: "A reference for the payment", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.accounting.sage.com/v3.1"; + }, + async _makeRequest({ + $ = this, path, headers, ...args + }) { + return axios($, { + ...args, + url: this._baseUrl() + path, + headers: { + ...headers, + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + "Content-Type": "application/json", + }, + }); + }, + async _paginatedRequest({ + params, ...args + }) { + const response = await this._makeRequest({ + params: { + ...params, + page: (params.page ?? 0) + 1, + }, + ...args, + }); + return response.$items || response.items || []; + }, + async listContactTypes(args) { + return this._paginatedRequest({ + path: "/contact_types", + ...args, + }); + }, + async listCurrencies(args) { + return this._paginatedRequest({ + path: "/currencies", + ...args, + }); + }, + async listContacts(args) { + return this._paginatedRequest({ + path: "/contacts", + ...args, + }); + }, + async createContact(args) { + return this._makeRequest({ + method: "POST", + path: "/contacts", + ...args, + }); + }, + async getContact({ + contactId, ...args + }) { + return this._makeRequest({ + path: `/contacts/${contactId}`, + ...args, + }); + }, + async updateContact({ + $, contactId, data, + }) { + return this._makeRequest({ + $, + path: `/contacts/${contactId}`, + method: "PUT", + data, + }); + }, + async deleteContact({ + contactId, ...args + }) { + return this._makeRequest({ + method: "DELETE", + path: `/contacts/${contactId}`, + ...args, + }); + }, + // Contact Payment API methods + async listTransactionTypes(args) { + return this._paginatedRequest({ + path: "/transaction_types", + ...args, + }); + }, + async listBankAccounts(args) { + return this._paginatedRequest({ + path: "/bank_accounts", + ...args, + }); + }, + async listPaymentMethods(args) { + return this._paginatedRequest({ + path: "/payment_methods", + ...args, + }); + }, + async listTaxRates(args) { + return this._paginatedRequest({ + path: "/tax_rates", + ...args, + }); + }, + async listLedgerAccounts(args) { + return this._paginatedRequest({ + path: "/ledger_accounts", + ...args, + }); + }, + async createContactPayment(args) { + return this._makeRequest({ + method: "POST", + path: "/contact_payments", + ...args, + }); + }, + async listSuppliers({ + params, ...args + }) { + return this._paginatedRequest({ + path: "/contacts", + params: { + contact_type_id: "SUPPLIER", + ...params, + }, + ...args, + }); + }, + async listCatalogItemTypes(args) { + return this._paginatedRequest({ + path: "/catalog_item_types", + ...args, + }); + }, + async createProduct(args) { + return this._makeRequest({ + method: "POST", + path: "/products", + ...args, + }); + }, + async listProducts(args) { + return this._paginatedRequest({ + path: "/products", + ...args, + }); + }, + async listServices(args) { + return this._paginatedRequest({ + path: "/services", + ...args, + }); + }, + async listContactPayments(args) { + return this._paginatedRequest({ + path: "/contact_payments", + ...args, + }); + }, + async listLedgerAccountTypes(args) { + return this._paginatedRequest({ + path: "/ledger_account_types", + ...args, + }); }, }, }; diff --git a/components/sage_accounting/sources/common/polling.mjs b/components/sage_accounting/sources/common/polling.mjs new file mode 100644 index 0000000000000..af39e737a9643 --- /dev/null +++ b/components/sage_accounting/sources/common/polling.mjs @@ -0,0 +1,55 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import sageAccounting from "../../sage_accounting.app.mjs"; + +export default { + props: { + sageAccounting, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getSavedIds() { + return this.db.get("savedIds") || []; + }, + _setSavedIds(ids) { + this.db.set("savedIds", ids); + }, + async startEvent(maxItems) { + const savedIds = this._getSavedIds(); + const items = await this.getItems(); + + const newIds = []; + for (const item of items) { + const id = this.getItemId(item); + if (!savedIds.includes(id)) { + const meta = this.generateMeta(item); + if (maxItems === undefined || (typeof maxItems === "number" && --maxItems >= 0)) { + this.$emit(item, meta); + } + newIds.push(id); + } + } + + if (newIds.length > 0) { + const ids = [ + ...savedIds, + ...newIds, + ].slice(-100); + this._setSavedIds(ids); + } + }, + }, + async run() { + await this.startEvent(); + }, + hooks: { + async deploy() { + await this.startEvent(5); + }, + }, +}; diff --git a/components/sage_accounting/sources/new-contact-payment-created/new-contact-payment-created.mjs b/components/sage_accounting/sources/new-contact-payment-created/new-contact-payment-created.mjs new file mode 100644 index 0000000000000..8dd9d2a1fe73a --- /dev/null +++ b/components/sage_accounting/sources/new-contact-payment-created/new-contact-payment-created.mjs @@ -0,0 +1,41 @@ +import common from "../common/polling.mjs"; + +export default { + ...common, + key: "sage_accounting-new-contact-payment-created", + name: "New Contact Payment Created", + description: "Emit new event when a contact payment is created in Sage Accounting. [See the documentation](https://developer.sage.com/accounting/reference/payments/#tag/Contact-Payments/operation/getContactPayments)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + generateMeta(payment) { + const id = this.getItemId(payment); + const summary = this.getItemSummary(payment); + return { + id, + summary: `New Payment to: ${summary}`, + ts: payment.created_at + ? new Date(payment.created_at).getTime() + : Date.now(), + }; + }, + getItemId(payment) { + return payment.id; + }, + getItemSummary(payment) { + const contactName = payment.contact?.displayed_as || payment.contact?.name || "Unknown Contact"; + return contactName; + }, + async getItems() { + const payments = await this.sageAccounting.listContactPayments({ + params: { + items_per_page: 100, + sort: "created_at:desc", + }, + }); + return payments || []; + }, + }, +}; diff --git a/components/sage_accounting/sources/new-product-created/new-product-created.mjs b/components/sage_accounting/sources/new-product-created/new-product-created.mjs new file mode 100644 index 0000000000000..38f967427abbe --- /dev/null +++ b/components/sage_accounting/sources/new-product-created/new-product-created.mjs @@ -0,0 +1,40 @@ +import common from "../common/polling.mjs"; + +export default { + ...common, + key: "sage_accounting-new-product-created", + name: "New Product Created", + description: "Emit new event when a product is created in Sage Accounting. [See the documentation](https://developer.sage.com/accounting/reference/products-services/#tag/Products/operation/getProducts)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + generateMeta(product) { + const id = this.getItemId(product); + const summary = this.getItemSummary(product); + return { + id, + summary: `New Product: ${summary}`, + ts: product.created_at + ? new Date(product.created_at).getTime() + : Date.now(), + }; + }, + getItemId(product) { + return product.id; + }, + getItemSummary(product) { + return product.description || product.displayed_as || `Product ${product.id}`; + }, + async getItems() { + const products = await this.sageAccounting.listProducts({ + params: { + items_per_page: 100, + sort: "created_at:desc", + }, + }); + return products || []; + }, + }, +}; diff --git a/components/sage_accounting/sources/new-service-created/new-service-created.mjs b/components/sage_accounting/sources/new-service-created/new-service-created.mjs new file mode 100644 index 0000000000000..7df04112b686e --- /dev/null +++ b/components/sage_accounting/sources/new-service-created/new-service-created.mjs @@ -0,0 +1,40 @@ +import common from "../common/polling.mjs"; + +export default { + ...common, + key: "sage_accounting-new-service-created", + name: "New Service Created", + description: "Emit new event when a service is created in Sage Accounting. [See the documentation](https://developer.sage.com/accounting/reference/products-services/#tag/Services/operation/getServices)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + generateMeta(service) { + const id = this.getItemId(service); + const summary = this.getItemSummary(service); + return { + id, + summary: `New Service: ${summary}`, + ts: service.created_at + ? new Date(service.created_at).getTime() + : Date.now(), + }; + }, + getItemId(service) { + return service.id; + }, + getItemSummary(service) { + return service.description || service.displayed_as || `Service ${service.id}`; + }, + async getItems() { + const services = await this.sageAccounting.listServices({ + params: { + items_per_page: 100, + sort: "created_at:desc", + }, + }); + return services || []; + }, + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 49e507710d189..95255b82c7840 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11634,7 +11634,11 @@ importers: components/ryver: {} - components/sage_accounting: {} + components/sage_accounting: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 components/sage_intacct: {} @@ -13309,8 +13313,7 @@ importers: components/switchboard: {} - components/sylius: - specifiers: {} + components/sylius: {} components/symbl_ai: dependencies: