🔨 Refactor product details

This commit is contained in:
Iván Ovejero 2021-06-21 14:15:04 +02:00
parent 120a8eec09
commit 0735316973
8 changed files with 238 additions and 94 deletions

View file

@ -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,

View file

@ -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),

View file

@ -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',

View file

@ -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',

View file

@ -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',

View file

@ -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',

View file

@ -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`;

View file

@ -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 }>;