We're software that helps growing brands & retailers grow and scale. Sync, sell and ship your products and inventory on online marketplaces and storefronts faster, easier and more accurately.

Learn more now

Testing Custom Automations

TABLE OF CONTENTS

Introduction


Are you looking to integrate with a custom supplier for drop shipping or automating inventory from an overstock warehouse? How about implementing proprietary pricing rules to increase sales or setting up special rules that determine which warehouse to drop ship from? Custom integrations are one way with SureDone you can accommodate a wide variety of use cases that fundamentally are a flavor of product or order import or export.


Once you better understand the Automation Engine and all of the potential eCommerce focused functionality you may quickly setup any number of integrations that include everything from accounting report exports all the way to complete channel integrations. However, with the ease that one can create powerful complex integrations also comes the ease to corrupt data from a complex automation not fully tested.


This guide will cover core principles for testing custom automations so they may be ran automatically with little concern for disrupting your workflow or data integrity.


Looking for guidance on how to test order drop shipping integrations? See the guide here!


Product Automations

Between product and order automations, product automations are definitely the easier of the two when it comes to testing both imports and exports. This is due to the result of product imports being a bulk file that may be reviewed for accuracy, and similarly the result of product exports being sent to a destination for review.


In general, you should be able to test an automation by heading over to the Run tab and check off "Run full update" and "Run in test mode".




Beyond testing to ensure the connection works, data is downloaded correctly, and fields are mapped appropriately - the majority of testing required is around "rules" that may be leveraged within an automation via "vendor_actions" or "suredone_actions". Let's look at the following "Pricing Rules" automation that includes rules for setting "price":


{
    "vendor": "suredone",
    "name": "Pricing Rules",
    "schedule": "30 */3 * * *",
    "type": "products",
    "action": "import",
    "file_configs":
    [
        {
            "search": "price:>0",
            "field_run":
            {
                "keystonesku": "keystonesku",
                "keystonecost": "keystonecost",
                "keystoneuspsable": "keystoneuspsable",
                "motorstatesku": "motorstatesku",
                "motorstatecost": "motorstatecost",
                "mapprice": "mapprice"
            },
            "field_map":
            {
                "price": "price",
                "keystonesellprice": "keystonesellprice",
                "keystoneshippingcharge": "keystoneshippingcharge",
                "motorstatesellprice": "motorstatesellprice",
                "motorstateshippingcharge": "motorstateshippingcharge"
            },
            "vendor_actions":
            [
                {
                    "setValue":
                    {
                        "price": "99999"
                    }
                },
                {
                    "search": "mapprice:>0 mapprice:<200 keystoneuspsable:=TRUE",
                    "setValue":
                    {
                        "keystoneshippingcharge": "9.99"
                    },
                    "sum":
                    {
                        "keystonesellprice":
                        [
                            "keystonecost",
                            "keystoneshippingcharge"
                        ]
                    },
                    "multiply":
                    {
                        "keystonesellprice":
                        [
                            "keystonesellprice",
                            "1.22"
                        ]
                    }
                },
                {
                    "search": "mapprice:>0 mapprice:<200 keystoneuspsable:=FALSE",
                    "setValue":
                    {
                        "keystoneshippingcharge": "150"
                    },
                    "sum":
                    {
                        "keystonesellprice":
                        [
                            "keystonecost",
                            "keystoneshippingcharge"
                        ]
                    },
                    "multiply":
                    {
                        "keystonesellprice":
                        [
                            "keystonesellprice",
                            "1.22"
                        ]
                    }
                },
                {
                    "search": "mapprice:>=200 keystoneuspsable:=TRUE",
                    "setValue":
                    {
                        "keystoneshippingcharge": "0"
                    },
                    "sum":
                    {
                        "keystonesellprice":
                        [
                            "keystonecost",
                            "keystoneshippingcharge"
                        ]
                    },
                    "multiply":
                    {
                        "keystonesellprice":
                        [
                            "keystonesellprice",
                            "1.22"
                        ]
                    }
                },
                {
                    "search": "mapprice:>=200 keystoneuspsable:=FALSE",
                    "setValue":
                    {
                        "keystoneshippingcharge": "150"
                    },
                    "sum":
                    {
                        "keystonesellprice":
                        [
                            "keystonecost",
                            "keystoneshippingcharge"
                        ]
                    },
                    "multiply":
                    {
                        "keystonesellprice":
                        [
                            "keystonesellprice",
                            "1.22"
                        ]
                    }
                },
                {
                    "search": "mapprice:>0 mapprice:<200",
                    "setValue":
                    {
                        "motorstateshippingcharge": "11.99"
                    },
                    "sum":
                    {
                        "motorstatesellprice":
                        [
                            "mapprice",
                            "motorstateshippingcharge"
                        ]
                    },
                    "multiply":
                    {
                        "motorstatesellprice":
                        [
                            "motorstatesellprice",
                            "1.22"
                        ]
                    }
                },
                {
                    "search": "mapprice:>=200",
                    "setValue":
                    {
                        "motorstateshippingcharge": "0"
                    },
                    "sum":
                    {
                        "motorstatesellprice":
                        [
                            "motorstatecost",
                            "motorstateshippingcharge"
                        ]
                    },
                    "multiply":
                    {
                        "motorstatesellprice":
                        [
                            "motorstatesellprice",
                            "1.22"
                        ]
                    }
                },

                {
                    "search": "keystonesellprice:>=motorstatesellprice",
                    "remapField":
                    {
                        "price": "keystonesellprice"
                    }
                },
                {
                    "search": "motorstatesellprice:>keystonesellprice",
                    "remapField":
                    {
                        "price": "motorstatesellprice"
                    }
                },

                {
                    "search": "mapprice:>keystonesellprice mapprice:>motorstatesellprice",
                    "remapField":
                    {
                        "price": "mapprice"
                    }
                }
            ],
            "field_format":
            {
                "motorstatesellprice":
                {
                    "decimalPlaces": 2
                },
                "keystonesellprice":
                {
                    "decimalPlaces": 2
                },
                "price":
                {
                    "decimalPlaces": 2
                }
            },
            "update": "edit"
        }
    ]
}

The first thing to notice in the config above is the "search" is bound to "price:>0". Searches in Automation Engine use the same format as used across the SureDone platform. When generating a test run, you should confirm that only products matching the search are part of the result set. Typos and incorrect syntax can lead to including more products for import/export than desired.


From there, take a look through the "vendor_actions" and notice that each rule is bound to a search, which essentially is a sub filter of the primary search. Few things to note here are:

  • Confirm that any fields used in all searches are part of "field_run" (fields to use in the automation) or "field_map" (fields the automation will import/export)
  • Create tests for at least 1 product that matches each of the criteria and look for the results in the test run result
  • Consider default values (rules without a "search" attached) as the first rule to ensure edge cases may be caught for review and refinement. In the example above, the default value sets the "price" to 99999, so if we see that in the debug file, we immediately know this item was unexpectedly not covered by the subsequent rules as it should have been
  • Note: rules are processed from top to bottom, and the result of each rule could affect the following rules
  • Note: IMPORT - for rules that affect pricing, you must include a final rule that sets a minimum sell price to prevent pricing that could cause business loss. For example, if your business relies on MAP Price, a rule that ensures no updates to products that do not have a MAP Price and/or a price that is set below the MAP Price.

By following the above steps, especially with consideration to default rules you should be able to run custom automations in a workflow that allows you to review issues and unexpected outcomes.


Order Automations


Since updates from order automations rely on internal API updates instead of bulk files, using the "Run in test mode" functionality may not help you understand if complex rules are working as expected. In order to effectively test order import integrations that leverage rules, create one or more orders with multiple order items that accommodate each of your rule sets. Let's look at the following "Order Import Rules" automation that includes rules for setting "source" to help determine which warehouse to drop ship from:


{
    "vendor": "SureDone",
    "name": "Order Import Rules",
    "draft": false,
    "disable_all": false,
    "shared": false,
    "active": false,
    "schedule": "*/3 * * * *",
    "type": "orders",
    "action": "import",
    "parameters":
    [
        {
            "name": "username",
            "value": "",
            "encrypted": false,
            "hidden_from_user": false,
            "label": "SureDone Username",
            "description": "Parameters are used to dynamic user input, in this case the SureDone Username"
        },
        {
            "name": "apitoken",
            "value": "",
            "encrypted": true,
            "hidden_from_user": false,
            "label": "SureDone API Token",
            "description": "Parameters are used to dynamic user input, in this case to securely store the SureDone API Token"
        }
    ],
    "connection":
    {
        "type": "http",
        "address": "https://api.suredone.com/v3/orders?lineitem=true",
        "method": "GET",
        "headers":
        {
            "Content-Type": "application/json",
            "x-auth-user": "{{username}}",
            "x-auth-token": "{{apitoken}}"
        },
        "paginate": 1
    },
    "file_configs":
    [
        {
            "identifier": "oid",
            "search": "itemstatus:=READY",
            "jsonpath": 1, // this config enables parsing of JSON in "field_map", "field_run" and more. Note the "$." notation is specific to this functionality.
            "field_map":
            {
                "source": "$.orders.*.source",
                "itemstatus": "$.orders.*.itemstatus"
            },
            "field_run":
            {
                "oid": "$.orders.*.order.oid",
                "itemid": "$.orders.*.itemid",
                "keystonecost": "$.orders.*.itemdetails.product.keystonecost",
                "keystoneshippingcharge": "$.orders.*.itemdetails.product.keystoneshippingcharge",
                "keystonestockv1": "$.orders.*.itemdetails.product.keystonestock",
                "keystonecostshippingv1": "$.orders.*.itemdetails.product.price",
                "motorstatecost": "$.orders.*.itemdetails.product.motorstatecost",
                "motorstateshippingcharge": "$.orders.*.itemdetails.product.motorstateshippingcharge",
                "motorstatestockv1": "$.orders.*.itemdetails.product.motorstatestock",
                "motorstatecostshippingv1": "$.orders.*.itemdetails.product.price",
                "lowestcost": "$.orders.*.itemdetails.product.price"
            },
            "placeholders_items": // used to set placeholder order item fields
            [
                "itemid",
                "keystonecost",
                "keystoneshippingcharge",
                "keystonestockv1",
                "keystonecostshippingv1",
                "motorstatecost",
                "motorstateshippingcharge",
                "motorstatestockv1",
                "motorstatecostshippingv1",
                "lowestcost"
            ],
            "vendor_actions":
            [
                {
                    "setValue":
                    {
                        "keystonecostshippingv1": 999999999,
                        "motorstatecostshippingv1": 999999999,
                        "itemstatus": "REVIEW"
                    }
                },
                {
                    "search": "keystonestockv1:>0",
                    "sum":
                    {
                        "keystonecostshippingv1":
                        [
                            "keystonecost",
                            "keystoneshippingcharge"
                        ]
                    }
                },
                {
                    "search": "motorstatestockv1:>0",
                    "sum":
                    {
                        "motorstatecostshippingv1":
                        [
                            "motorstatecost",
                            "motorstateshippingcharge"
                        ]
                    }
                },
                {
                    "search": "(keystonestockv1:>0 motorstatestockv1:>0)",
                    "min":
                    {
                        "lowestcost":
                        [
                            "keystonecostshippingv1",
                            "motorstatecostshippingv1",
                            "xdpcostshippingv1",
                            "transamericancostshippingv1",
                            "premiercostshippingv1",
                            "turn14costshippingv1",
                            "holleycostshippingv1"
                        ]
                    }
                },
                {
                    "search": "keystonecostshippingv1:<=lowestcost keystonecostshippingv1:>0",
                    "setValue":
                    {
                        "source": "keystonestock",
                        "itemstatus": ""
                    }
                },
                {
                    "search": "motorstatecostshippingv1:<=lowestcost motorstatecostshippingv1:>0",
                    "setValue":
                    {
                        "source": "motorstatestock",
                        "itemstatus": ""
                    }
                },
                {
                    "search": "(source:=component_stock source:=all_stock source:=)",
                    "setValue":
                    {
                        "itemstatus": "REVIEW"
                    }
                }
            ],
            "update": true
        }
    ]
}

The first thing to notice in the config above is the "search" is bound to "itemstatus:=READY". Searches in Automation Engine use the same format as used across the SureDone platform. When generating a test run, you should confirm that only orders matching the search are part of the result set. Typos and incorrect syntax can lead to including more orders/order items for import/export than desired.


From there, take a look through the "vendor_actions" and notice that each rule is bound to a search, which essentially is a sub filter of the primary search. Few things to note here are:


  • During testing, ensure only the test orders matches the automation "search"
  • Confirm that any fields used in all searches are part of "field_run" (fields to use in the automation) or "field_map" (fields the automation will import/export)
  • Create tests for at least 1 order item that matches each of the criteria on one or more test orders
  • Consider default values (rules without a "search" attached) as the first rule to ensure edge cases may be caught for review and refinement. In the example above, the default value sets the "itemstatus" to "REVIEW", this ensures any unconsidered edge cases are caught
  • Note: rules are processed from top to bottom, and the result of each rule could affect the following rules
  • Then, use the "Run" functionality in production to confirm the order rules work as expected

By following the above steps, especially with consideration to default rules you should be able to run custom automations in a workflow that allows you to review issues and unexpected outcomes.