Example #1
0
        private void EnlistDeliveryExtensions()
        {
            var extensions = new List <JobTaskExtension>();

            // Delivery task extension

            /* INFO: Our actionable states are FAILED and RETURNED.
             * We want to extend functionalities on these two states of course.
             **/
            var actionableState = JobTaskState.FAILED | JobTaskState.RETURNED;

            // Making the condition to an expression so that it can be used in need.
            Expression <Func <JobTask, bool> > conditionExpression =
                task => (actionableState & task.State) == task.State;

            var deliveryJobTaskExtension = new JobTaskExtension(
                OrderTypes.Delivery,
                JobTaskTypes.DELIVERY,
                conditionExpression)
            {
                ExecuteExtension = (job, task) =>
                {
                    // Corner case checks
                    if (job == null)
                    {
                        throw new ArgumentNullException(nameof(job));
                    }
                    if (task?.Type != JobTaskTypes.DELIVERY)
                    {
                        throw new ArgumentNullException(nameof(task));
                    }

                    /* Get index of current task. We need to know where the task is at
                     * Because otherwise we wont be able to inject new Tasks inside.
                     */

                    var index = job.Tasks.IndexOf(task);
                    if (index == -1)
                    {
                        throw new ArgumentException(nameof(task));
                    }

                    // Casting the task as deliveryTask since this extension is attached to this specific job task type
                    var deliveryTask = task as DeliveryTask;
                    if (deliveryTask == null)
                    {
                        throw new ArgumentNullException(nameof(deliveryTask));
                    }

                    /* I know a lot of these code looks like boilerplate and replicated. I can consolidate those
                     * but they do hurt readability and that's why I allowed to have a little replicated code here
                     */

                    if (deliveryTask.State == JobTaskState.FAILED)
                    {
                        /* INFO: If the delivery task has reached state FAILED,
                         * it is our job to make sure the next task should retry to send
                         * it again
                         */

                        // Increase Job Attempt
                        job.AttemptCount++;

                        // Create a delivery task that will send the product back since delivery has failed
                        var newDeliveryTask = deliveryTask.GenerateRetryTask();
                        newDeliveryTask.SetPredecessor(deliveryTask, validateDependency: false);

                        InsertTaskIntoTaskChain(job, index, newDeliveryTask);
                    }
                    else if (deliveryTask.State == JobTaskState.RETURNED)
                    {
                        /* INFO: This means the delivery has ended up in RETURNED state.
                         * Since this is a regular Delivery job, that also means this is possibly a
                         * B2C job. Sending it back to office its only thing we can potentially do
                         * at least for now
                         */

                        deliveryTask.IsTerminatingTask = false;

                        var newDeliveryTask = deliveryTask.GenerateReturnTask();
                        newDeliveryTask.SetPredecessor(deliveryTask, validateDependency: false);

                        var propSettings = Settings.Get <ProprietorSettings>();
                        if (propSettings?.Address == null)
                        {
                            throw new InvalidOperationException("ProprietorSettings is missing Address");
                        }
                        newDeliveryTask.To = propSettings.Address;

                        RemoveTasksAfterIndex(job, index);
                        job.AddTask(newDeliveryTask, hookPropertyChangedEvent: true);
                        job.TerminalTask = newDeliveryTask;
                    }

                    return(job);
                }
            };

            extensions.Add(deliveryJobTaskExtension);
            this.ExtensionsDictionary.Add(OrderTypes.Delivery, extensions);
        }
Example #2
0
        // Enterprise Delivery
        private void EnlistClassifiedDeliveryExtensions()
        {
            var extensions = new List <JobTaskExtension>();

            // Delivery task extension

            /* INFO: Our actionable states are FAILED and RETURNED.
             * We want to extend functionalities on these two states of course.
             **/
            var actionableState = JobTaskState.FAILED | JobTaskState.RETURNED;

            // Making the condition to an expression so that it can be used in need.
            Expression <Func <JobTask, bool> > conditionExpression =
                task => (actionableState & task.State) == task.State;

            /* INFO: This is where we construct our delivery job task extension
             * for the Classified delivery order and its associated job.
             * We enlisted proper order and job task type for this in the constructor.
             * We wrote the actions to be executed in the ExecuteAction Func
             */
            var deliveryJobTaskExtension = new JobTaskExtension(
                OrderTypes.ClassifiedDelivery,
                JobTaskTypes.DELIVERY,
                conditionExpression)
            {
                ExecuteExtension = (job, task) =>
                {
                    // Corner case checks
                    if (job == null)
                    {
                        throw new ArgumentNullException(nameof(job));
                    }
                    if (task?.Type != JobTaskTypes.DELIVERY)
                    {
                        throw new ArgumentNullException(nameof(task));
                    }

                    /* Get index of current task. We need to know where the task is at
                     * Because otherwise we wont be able to inject new Tasks inside.
                     */

                    var index = job.Tasks.IndexOf(task);
                    if (index == -1)
                    {
                        throw new ArgumentException(nameof(task));
                    }

                    // Casting the task as deliveryTask since this extension is attached to this specific job task type
                    var deliveryTask = task as DeliveryTask;
                    if (deliveryTask == null)
                    {
                        throw new ArgumentNullException(nameof(deliveryTask));
                    }


                    if (deliveryTask.State == JobTaskState.FAILED)
                    {
                        /* INFO: If the delivery task has reached state FAILED
                         * that only means the next task should try
                         * to resend the pacakage again.
                         *
                         * That also means that we have to push this new job before
                         * any task after the current task and increase the job attempt.
                         * */

                        // Increase Job Attempt
                        job.AttemptCount++;

                        // Create a delivery task that will send the product back since delivery has failed
                        var newDeliveryTask = deliveryTask.GenerateRetryTask();
                        newDeliveryTask.SetPredecessor(deliveryTask, validateDependency: false);

                        InsertTaskIntoTaskChain(job, index, newDeliveryTask);
                    }
                    else if (deliveryTask.State == JobTaskState.RETURNED)
                    {
                        /* This means the job has returned. The target site for the
                         * job has declined the package and now we need to handle this.
                         * There are two ways to handle this. First, we need to check
                         * if this is already a return task. If it is, the product should
                         * be returned to the nearest office.
                         *
                         * If this is already not a return job then we should try to return it
                         * to the proper owner. For Enterprise variant of classified delivery
                         * the product should go back to the actual owner/provider of the product,
                         * in this case the enterprise user.
                         *
                         * For the default version of the classified delivery this should go
                         * back to the actual seller of the product.
                         *
                         * Both of these cases can be handled easily since the addresses are already there
                         * */

                        deliveryTask.IsTerminatingTask = false;

                        var newDeliveryTask = deliveryTask.GenerateReturnTask();
                        newDeliveryTask.SetPredecessor(deliveryTask, validateDependency: false);


                        if (deliveryTask.Variant == DeliveryTaskVariants.Return)
                        {
                            // Delivery Task is already a return task.
                            // Lets get it back straight to the HQ

                            // Generating a return task for this and modify the To field to point to our office

                            var propSettings = Settings.Get <ProprietorSettings>();
                            if (propSettings?.Address == null)
                            {
                                throw new InvalidOperationException("ProprietorSettings is missing Address");
                            }
                            newDeliveryTask.To = propSettings.Address;
                        }

                        /* Since this will be the last entry to the Tasks, we need to
                         * remove all the tasks that we dont need anymore which are listed
                         * after this and set this one to be the last task
                         * */

                        RemoveTasksAfterIndex(job, index);
                        job.AddTask(newDeliveryTask, hookPropertyChangedEvent: true);
                        job.TerminalTask = newDeliveryTask;
                    }

                    return(job);
                }
            };

            extensions.Add(deliveryJobTaskExtension);
            this.ExtensionsDictionary.Add(OrderTypes.ClassifiedDelivery, extensions);
        }