mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
🔨 Refactor product details
This commit is contained in:
parent
120a8eec09
commit
0735316973
|
@ -66,6 +66,7 @@ export async function zohoApiRequest(
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log(JSON.stringify(options, null, 2));
|
||||||
const responseData = await this.helpers.requestOAuth2?.call(this, 'zohoOAuth2Api', options);
|
const responseData = await this.helpers.requestOAuth2?.call(this, 'zohoOAuth2Api', options);
|
||||||
|
|
||||||
if (responseData === undefined) return [];
|
if (responseData === undefined) return [];
|
||||||
|
@ -138,6 +139,19 @@ export function throwOnEmptyUpdate(this: IExecuteFunctions, resource: CamelCaseR
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function throwOnMissingProducts(
|
||||||
|
this: IExecuteFunctions,
|
||||||
|
resource: CamelCaseResource,
|
||||||
|
productDetails: ProductDetails,
|
||||||
|
) {
|
||||||
|
if (!productDetails.length) {
|
||||||
|
throw new NodeOperationError(
|
||||||
|
this.getNode(),
|
||||||
|
`Please enter at least one product for the ${resource}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function throwOnErrorStatus(
|
export function throwOnErrorStatus(
|
||||||
this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions,
|
this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions,
|
||||||
responseData: { data?: Array<{ status: string, message: string }> },
|
responseData: { data?: Array<{ status: string, message: string }> },
|
||||||
|
@ -168,6 +182,23 @@ export const adjustProductDetails = (productDetails: ProductDetails) => {
|
||||||
// additional field adjusters
|
// additional field adjusters
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Place a product ID at a nested position in a product details field.
|
||||||
|
*
|
||||||
|
* Only for updating products from Invoice, Purchase Order, Quote, and Sales Order.
|
||||||
|
*/
|
||||||
|
export const adjustProductDetailsOnUpdate = (allFields: AllFields) => {
|
||||||
|
if (!allFields.Product_Details) return allFields;
|
||||||
|
|
||||||
|
return allFields.Product_Details.map(p => {
|
||||||
|
return {
|
||||||
|
...omit('product', p),
|
||||||
|
product: { id: p.id },
|
||||||
|
quantity: p.quantity || 1,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Place a location field's contents at the top level of the payload.
|
* Place a location field's contents at the top level of the payload.
|
||||||
*/
|
*/
|
||||||
|
@ -268,6 +299,11 @@ export const adjustInvoicePayload = flow(
|
||||||
adjustCustomFields,
|
adjustCustomFields,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const adjustInvoicePayloadOnUpdate = flow(
|
||||||
|
adjustInvoicePayload,
|
||||||
|
adjustProductDetailsOnUpdate,
|
||||||
|
);
|
||||||
|
|
||||||
export const adjustLeadPayload = flow(
|
export const adjustLeadPayload = flow(
|
||||||
adjustAddressFields,
|
adjustAddressFields,
|
||||||
adjustCustomFields,
|
adjustCustomFields,
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
adjustContactPayload,
|
adjustContactPayload,
|
||||||
adjustDealPayload,
|
adjustDealPayload,
|
||||||
adjustInvoicePayload,
|
adjustInvoicePayload,
|
||||||
|
adjustInvoicePayloadOnUpdate,
|
||||||
adjustLeadPayload,
|
adjustLeadPayload,
|
||||||
adjustProductDetails,
|
adjustProductDetails,
|
||||||
adjustProductPayload,
|
adjustProductPayload,
|
||||||
|
@ -27,6 +28,7 @@ import {
|
||||||
getPicklistOptions,
|
getPicklistOptions,
|
||||||
handleListing,
|
handleListing,
|
||||||
throwOnEmptyUpdate,
|
throwOnEmptyUpdate,
|
||||||
|
throwOnMissingProducts,
|
||||||
toLoadOptions,
|
toLoadOptions,
|
||||||
zohoApiRequest,
|
zohoApiRequest,
|
||||||
zohoApiRequestAllItems,
|
zohoApiRequestAllItems,
|
||||||
|
@ -669,6 +671,8 @@ export class ZohoCrm implements INodeType {
|
||||||
|
|
||||||
const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails;
|
const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails;
|
||||||
|
|
||||||
|
throwOnMissingProducts.call(this, resource, productDetails);
|
||||||
|
|
||||||
const body: IDataObject = {
|
const body: IDataObject = {
|
||||||
Subject: this.getNodeParameter('subject', i),
|
Subject: this.getNodeParameter('subject', i),
|
||||||
Product_Details: adjustProductDetails(productDetails),
|
Product_Details: adjustProductDetails(productDetails),
|
||||||
|
@ -730,7 +734,7 @@ export class ZohoCrm implements INodeType {
|
||||||
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||||
|
|
||||||
if (Object.keys(updateFields).length) {
|
if (Object.keys(updateFields).length) {
|
||||||
Object.assign(body, adjustInvoicePayload(updateFields));
|
Object.assign(body, adjustInvoicePayloadOnUpdate(updateFields));
|
||||||
} else {
|
} else {
|
||||||
throwOnEmptyUpdate.call(this, resource);
|
throwOnEmptyUpdate.call(this, resource);
|
||||||
}
|
}
|
||||||
|
@ -738,6 +742,7 @@ export class ZohoCrm implements INodeType {
|
||||||
const invoiceId = this.getNodeParameter('invoiceId', i);
|
const invoiceId = this.getNodeParameter('invoiceId', i);
|
||||||
|
|
||||||
const endpoint = `/invoices/${invoiceId}`;
|
const endpoint = `/invoices/${invoiceId}`;
|
||||||
|
|
||||||
responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body);
|
responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body);
|
||||||
responseData = responseData.data;
|
responseData = responseData.data;
|
||||||
|
|
||||||
|
@ -760,7 +765,7 @@ export class ZohoCrm implements INodeType {
|
||||||
Object.assign(body, adjustInvoicePayload(additionalFields));
|
Object.assign(body, adjustInvoicePayload(additionalFields));
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = await zohoApiRequest.call(this, 'POST', `/invoices/upsert`, body);
|
responseData = await zohoApiRequest.call(this, 'POST', '/invoices/upsert', body);
|
||||||
responseData = responseData.data;
|
responseData = responseData.data;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -994,6 +999,8 @@ export class ZohoCrm implements INodeType {
|
||||||
|
|
||||||
const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails;
|
const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails;
|
||||||
|
|
||||||
|
throwOnMissingProducts.call(this, resource, productDetails);
|
||||||
|
|
||||||
const body: IDataObject = {
|
const body: IDataObject = {
|
||||||
Subject: this.getNodeParameter('subject', i),
|
Subject: this.getNodeParameter('subject', i),
|
||||||
Vendor_Name: { id: this.getNodeParameter('vendorId', i) },
|
Vendor_Name: { id: this.getNodeParameter('vendorId', i) },
|
||||||
|
@ -1109,6 +1116,8 @@ export class ZohoCrm implements INodeType {
|
||||||
|
|
||||||
const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails;
|
const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails;
|
||||||
|
|
||||||
|
throwOnMissingProducts.call(this, resource, productDetails);
|
||||||
|
|
||||||
const body: IDataObject = {
|
const body: IDataObject = {
|
||||||
Subject: this.getNodeParameter('subject', i),
|
Subject: this.getNodeParameter('subject', i),
|
||||||
Product_Details: adjustProductDetails(productDetails),
|
Product_Details: adjustProductDetails(productDetails),
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
currencies,
|
currencies,
|
||||||
makeCustomFieldsFixedCollection,
|
makeCustomFieldsFixedCollection,
|
||||||
makeGetAllFields,
|
makeGetAllFields,
|
||||||
makeProductDetails,
|
productDetailsOptions,
|
||||||
shippingAddress,
|
shippingAddress,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
|
@ -103,7 +103,29 @@ export const invoiceFields = [
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// invoice: create + upsert
|
// invoice: create + upsert
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
makeProductDetails('invoice'),
|
{
|
||||||
|
displayName: 'Products',
|
||||||
|
name: 'Product_Details',
|
||||||
|
type: 'collection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
multipleValueButtonText: 'Add Product',
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: productDetailsOptions,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'invoice',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
'upsert',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
name: 'additionalFields',
|
name: 'additionalFields',
|
||||||
|
@ -377,6 +399,18 @@ export const invoiceFields = [
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Products',
|
||||||
|
name: 'Product_Details',
|
||||||
|
type: 'collection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
multipleValueButtonText: 'Add Product',
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: productDetailsOptions,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Sales Commission',
|
displayName: 'Sales Commission',
|
||||||
name: 'Sales_Commission',
|
name: 'Sales_Commission',
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
currencies,
|
currencies,
|
||||||
makeCustomFieldsFixedCollection,
|
makeCustomFieldsFixedCollection,
|
||||||
makeGetAllFields,
|
makeGetAllFields,
|
||||||
makeProductDetails,
|
productDetailsOptions,
|
||||||
shippingAddress,
|
shippingAddress,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
|
@ -124,7 +124,29 @@ export const purchaseOrderFields = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
makeProductDetails('purchaseOrder'),
|
{
|
||||||
|
displayName: 'Products',
|
||||||
|
name: 'Product_Details',
|
||||||
|
type: 'collection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
multipleValueButtonText: 'Add Product',
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: productDetailsOptions,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'purchaseOrder',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
'upsert',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
name: 'additionalFields',
|
name: 'additionalFields',
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
currencies,
|
currencies,
|
||||||
makeCustomFieldsFixedCollection,
|
makeCustomFieldsFixedCollection,
|
||||||
makeGetAllFields,
|
makeGetAllFields,
|
||||||
makeProductDetails,
|
productDetailsOptions,
|
||||||
shippingAddress,
|
shippingAddress,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
|
@ -103,7 +103,29 @@ export const quoteFields = [
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// quote: create + upsert
|
// quote: create + upsert
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
makeProductDetails('quote'),
|
{
|
||||||
|
displayName: 'Products',
|
||||||
|
name: 'Product_Details',
|
||||||
|
type: 'collection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
multipleValueButtonText: 'Add Product',
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: productDetailsOptions,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'quote',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
'upsert',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
name: 'additionalFields',
|
name: 'additionalFields',
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
currencies,
|
currencies,
|
||||||
makeCustomFieldsFixedCollection,
|
makeCustomFieldsFixedCollection,
|
||||||
makeGetAllFields,
|
makeGetAllFields,
|
||||||
makeProductDetails,
|
productDetailsOptions,
|
||||||
shippingAddress,
|
shippingAddress,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
|
@ -128,7 +128,29 @@ export const salesOrderFields = [
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// salesOrder: create + upsert
|
// salesOrder: create + upsert
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
makeProductDetails('salesOrder'),
|
{
|
||||||
|
displayName: 'Products',
|
||||||
|
name: 'Product_Details',
|
||||||
|
type: 'collection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
multipleValueButtonText: 'Add Product',
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: productDetailsOptions,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'salesOrder',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
'upsert',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
name: 'additionalFields',
|
name: 'additionalFields',
|
||||||
|
|
|
@ -225,93 +225,91 @@ export const address = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const makeProductDetails = (resource: CamelCaseResource) => ({
|
// displayName: 'Products',
|
||||||
displayName: 'Products',
|
// name: 'Product_Details',
|
||||||
name: 'Product_Details',
|
// type: 'collection',
|
||||||
type: 'collection',
|
// typeOptions: {
|
||||||
typeOptions: {
|
// multipleValues: true,
|
||||||
multipleValues: true,
|
// multipleValueButtonText: 'Add Product',
|
||||||
multipleValueButtonText: 'Add Product',
|
// },
|
||||||
|
// default: {},
|
||||||
|
// placeholder: 'Add Field',
|
||||||
|
// displayOptions: {
|
||||||
|
// show: {
|
||||||
|
// resource: [
|
||||||
|
// resource,
|
||||||
|
// ],
|
||||||
|
// operation: [
|
||||||
|
// operation,
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
|
||||||
|
export const productDetailsOptions = [
|
||||||
|
{
|
||||||
|
displayName: 'List Price',
|
||||||
|
name: 'list_price',
|
||||||
|
type: 'number',
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
default: {},
|
{
|
||||||
placeholder: 'Add Field',
|
displayName: 'Product ID',
|
||||||
displayOptions: {
|
name: 'id',
|
||||||
show: {
|
type: 'options',
|
||||||
resource: [
|
default: [],
|
||||||
resource,
|
typeOptions: {
|
||||||
],
|
loadOptionsMethod: 'getProducts',
|
||||||
operation: [
|
|
||||||
'create',
|
|
||||||
'upsert',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: [
|
{
|
||||||
{
|
displayName: 'Product Description',
|
||||||
displayName: 'List Price',
|
name: 'product_description',
|
||||||
name: 'list_price',
|
type: 'string',
|
||||||
type: 'number',
|
default: '',
|
||||||
default: '',
|
},
|
||||||
},
|
{
|
||||||
{
|
displayName: 'Quantity',
|
||||||
displayName: 'Product ID',
|
name: 'quantity',
|
||||||
name: 'id',
|
type: 'number',
|
||||||
type: 'options',
|
default: '',
|
||||||
default: [],
|
},
|
||||||
typeOptions: {
|
{
|
||||||
loadOptionsMethod: 'getProducts',
|
displayName: 'Quantity in Stock',
|
||||||
},
|
name: 'quantity_in_stock',
|
||||||
},
|
type: 'number',
|
||||||
{
|
default: '',
|
||||||
displayName: 'Product Description',
|
},
|
||||||
name: 'product_description',
|
{
|
||||||
type: 'string',
|
displayName: 'Tax',
|
||||||
default: '',
|
name: 'Tax',
|
||||||
},
|
type: 'number',
|
||||||
{
|
default: '',
|
||||||
displayName: 'Quantity',
|
},
|
||||||
name: 'quantity',
|
{
|
||||||
type: 'number',
|
displayName: 'Total',
|
||||||
default: '',
|
name: 'total',
|
||||||
},
|
type: 'number',
|
||||||
{
|
default: '',
|
||||||
displayName: 'Quantity in Stock',
|
},
|
||||||
name: 'quantity_in_stock',
|
{
|
||||||
type: 'number',
|
displayName: 'Total After Discount',
|
||||||
default: '',
|
name: 'total_after_discount',
|
||||||
},
|
type: 'number',
|
||||||
{
|
default: '',
|
||||||
displayName: 'Tax',
|
},
|
||||||
name: 'Tax',
|
{
|
||||||
type: 'number',
|
displayName: 'Total (Net)',
|
||||||
default: '',
|
name: 'net_total',
|
||||||
},
|
type: 'number',
|
||||||
{
|
default: '',
|
||||||
displayName: 'Total',
|
},
|
||||||
name: 'total',
|
{
|
||||||
type: 'number',
|
displayName: 'Unit Price',
|
||||||
default: '',
|
name: 'unit_price',
|
||||||
},
|
type: 'number',
|
||||||
{
|
default: '',
|
||||||
displayName: 'Total After Discount',
|
},
|
||||||
name: 'total_after_discount',
|
];
|
||||||
type: 'number',
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Total (Net)',
|
|
||||||
name: 'net_total',
|
|
||||||
type: 'number',
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Unit Price',
|
|
||||||
name: 'unit_price',
|
|
||||||
type: 'number',
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
export const makeGetAllFields = (resource: CamelCaseResource) => {
|
export const makeGetAllFields = (resource: CamelCaseResource) => {
|
||||||
const loadOptionsMethod = `get${capitalizeInitial(resource)}Fields`;
|
const loadOptionsMethod = `get${capitalizeInitial(resource)}Fields`;
|
||||||
|
|
1
packages/nodes-base/nodes/Zoho/types.d.ts
vendored
1
packages/nodes-base/nodes/Zoho/types.d.ts
vendored
|
@ -43,6 +43,7 @@ export type AllFields =
|
||||||
{ Account?: { subfields: { id: string; name: string; } } } &
|
{ Account?: { subfields: { id: string; name: string; } } } &
|
||||||
{ [key in 'accountId' | 'contactId' | 'dealId']?: string } &
|
{ [key in 'accountId' | 'contactId' | 'dealId']?: string } &
|
||||||
{ customFields?: { customFields: Array<{ fieldId: string; value: string; }> } } &
|
{ customFields?: { customFields: Array<{ fieldId: string; value: string; }> } } &
|
||||||
|
{ Product_Details?: ProductDetails } &
|
||||||
IDataObject;
|
IDataObject;
|
||||||
|
|
||||||
export type ProductDetails = Array<{ id: string, quantity: number }>;
|
export type ProductDetails = Array<{ id: string, quantity: number }>;
|
||||||
|
|
Loading…
Reference in a new issue