Most Powerful Open Source ERP

How To Create ERP5 Trade From Scratch

showing creation of a base version of ERP5 Trade and how to use simulation in this case
  • Last Update:2016-02-11
  • Version:001
  • Language:en

This how to will show how to create a simple version of ERP5 Trade from scratch.

Table of Contents

What is ERP5 Trade ?

ERP5 Trade is a Business Template used to manage customer's and supplier's orders.

What is the purpose of this HowTo ?

This HowTo will describe how to create a basic version of ERP5 Trade from scratch, and how we use the simulation in this case. We will assume that user is used to ERP5, and masters other HowTos, like

Warning

This HowTo is only for a training purpose. If you want to use ERP5 Trade on a production site, don't follow this HowTo and download the erp5_trade Business Template.

Step One: Prepare the site

Install ERP5

Download ERP5 and install it.

Create the Nodes

Install the business template erp5_base, which allows us to create node objects in ERP5.

Create 2 organisations,

  • one called OurOrganisation representing us
  • one called TheCustomer representing the customer
organisations_created.png

Create a product

Install the business template erp5_pdm, which allows us to create resource objects in ERP5.

Now, we can create a product with the title AGreatProduct in the Products Module.

product.png

Step Two: create an order

Create the Order Module

Create the module where the orders will be entered. Give those parameters to the module creation script:

  • Module ID: order_module
  • Module Portal Type: Order Module
  • Module Title: Orders
  • Object Portal Type: Order
  • Object Title: Order
  • Portal Skins Folder: basic_trade
order_module_creation.png

Configure Order Portal Type

Order will represent the contract between us and the customer. In the ERP5 Model, Order will be considered as Movement, so, we need to modify the factory method. We will use the Document Order, which does not catalog his content's movement in the stock table. Go in portal_types tool, and modify the portal type Order like this:

  • Product meta type: ERP5 Order
  • Product factory method: addOrder
  • In 'Groups' select 'order'

When creating, we will add the Portal Type Order to the Portal Method getPortalOrderTypeList by adding order to the Groups attribute on the portal type level. This configuration permits to make acquistion between line and order.

order_factory_method.png

Configure Order Module

We can now filter the content types in the Order Module. Check Filter content types?, and select Order in Allowed content types on the Order Module's Portal Type.

module_filter_content_type.png

Create Order Workflow

First of all, associate edit_workflow to the Order Portal Type.

Then, we need to create manually the order_workflow. This workflow will be used to generated the simulation related to the order, and to build the related packing list. Add it in portal_workflow. As we will create movement, use the state variable name: simulation_state. Now, it's time to create our states: draft, planned, confirmed. Create the transitions:

  • plan from draft to planned with its trigger type as 'Initiated by WorkflowMethod'
  • confirm from planned to confirmed with its trigger type as 'Initiated by WorkflowMethod'
  • plan_action with destination as `(Remain in State)', trigger type as 'Initiated by User Action', name as 'Plan Order' and URL as '%(content_url)s/!BaseWorkflow_viewWorkflowActionDialog?workflow_action=plan_action'
  • confirm_action with destination as `(Remain in State)', trigger type as 'Initiated by User Action', name as 'Confirm Order' and URL as '%(content_url)s/!BaseWorkflow_viewWorkflowActionDialog?workflow_action=confirm_action'
order_workflow_graph.png

Finally, associate the order_workflow to the portal type Order.

order_workflows.png

As we need to generate the workflow methods, the easiest way currently is to restart the site.

Display the Order

Modify Order_view, in order to display the attributes:

  • title, which is the reference of the order:
    • add StringField with Id "my_title"
  • source, where the products come from (an organisation representing ourself)
    • add RelationStringField with Id "my_source_title", and:
      • set 'Base Category' to "source"
      • set 'Portal Type' to "Organisation"
      • set 'Catalog Index' to "title"
  • destination, where the products are going to (an organisation representing the customer)
    • add RelationStringField with Id "my_destination_title", and:
      • set 'Base Category' to "destination"
      • set 'Portal Type' to "Organisation"
      • set 'Catalog Index' to "title"
  • start_date, when we send the products:
    • add DateTimeField with Id "my_start_date"
    • set 'Display date only'
    • set 'Input style' to "list"
  • stop_date, when the products arrived to the customer
    • add DateTimeField with Id "my_stop_date"
    • set 'Display date only'
    • set 'Input style' to "list"
  • simulation state
    • add StringField with Id "my_simulation_state"
    • unset 'Editable'
  • in settings set 'Form action' to 'Base_edit'

Note: You migth have to restart zope before continuing.

Create the order object

At this point, we can create an Order in the Order Module, with those attribute's values:

  • title: 0000001
  • source: OurOrganisation
  • destination: TheCustomer
  • start_date: 2006/05/09
  • stop_date: 2006/05/19
order_created.png

Create the Order Line portal type

In order to enter the products we are selling, we need to create the portal type Order Line, with default type information: ERP5Type: ERP5 Order Line. When creating, add order_movement to the Groups, in order to make acquisition between line and simulation movement.

create_sale_order_line_portal_type.png

Add Order Line as available content type in Order

Add Order Line in Allowed content types on Order.

content_type_of_order.png

Create OrderLine_view

Modify OrderLine_view, in order to display the attributes:

  • resource, which is the product we are selling (the object in the Products module):
    • add 'RelationStringField' with Id "my_resource_title"
    • set 'Base Category' to "resource"
    • set 'Portal Type' to "Product"
    • set 'Catalog Index' to "title"
  • quantity, as an integer:
    • add 'IntegerField' with Id "my_quantity"
  • in Settings set 'Form action' to "Base_edit"
order_line_view.png

Create an OrderLine in the order 000001

At this point, we can create an OrderLine in our Order, with those attribute's values:

  • resource: AGreatProduct
  • quantity, 10
order_line_created.png

Modify Order_view, in order to view the lines

Add a listboxin Order_view, in order to display the order lines on the order level.

  • in 'Columns' put lines:
    • id
    • resource_title
    • quantity
  • set 'List Method' to "contentValues"
order_lines_listbox.png

Step Three: Generate the simulation

Create the OrderRule portal type

The simulation rule representing the order/packing list is called Order Rule in ERP5. If we want to use it, we have to add a new portal type Order Rule, with the default type information set to ERP5Type: ERP5 Order Rule.

order_rule_portal_type_creation.png

Create an OrderRule instance

All rule's instances are saved in portal_rules. Add Order Rule as allowed content type in Rule Tool, and create a Order Rule instance via the default view of portal_rules.

Note: Do not worry, when you see Error Type: KeyError Error Value: 'OrderRule_view', simply change the form on the "view" action = Rule_view in your portal type "Order Rule".

order_rule_creation.png

Configure order_workflow

Time to configure the order_workflow, in order to make it create the simulation as soon as we change the state to planned. Add a workflow script called Order_createAppliedRule in order_workflow.

Give to this script the parameters: state_change, **kw.

Then, add this code:

Toggle line numbers
   1 order = state_change['object']
   2 # The ID of the rule in portal_rules we want to use.
   3 order_rule_id = '1'
   4 # Create the applied rule in activity.
   5 tag = order.getPath() + '_firstUpdateAppliedRule'
   6 activate_kw = {'tag':tag, 'priority':3}
   7 order.activate(tag=tag).updateAppliedRule(
   8                           rule_id=order_rule_id,
   9                           activate_kw=activate_kw)
Toggle line numbers order = state_change['object'] # The ID of the rule in portal_rules we want to use. order_rule_id = '1' # Create the applied rule in activity. tag = order.getPath() + '_firstUpdateAppliedRule' activate_kw = {'tag':tag, 'priority':3} order.activate(tag=tag).updateAppliedRule( rule_id=order_rule_id, activate_kw=activate_kw)

Finally, set Order_createAppliedRule as the Script (after) of the plan transition.

Change the state of order 000001

Modify the state from draft to planned. After a few seconds, click on the Metadata tab of the Order. You will see /erp5/portal_simulation/1 as a related object of the order.

Check the simulation

In portal simulation, there is one Applied Rule in relation with the 'Order Rule' and our order.

applied_rule.png

It contains one simulation related to the order line. The simulation movement copied his attributes from the order line (currently, the screenshot is outdated).

simulation_movement1.png

Step Four: Generate the Packing List

Create the Packing List Module

Create the module where the packing list will be generated. Give those parameters to the module creation script:

  • Module ID: packing_list_module
  • Module Portal Type: Packing List Module
  • Module Title: Packing Lists
  • Object Portal Type: Packing List
  • Object Title: Packing List
  • Portal Skins Folder: basic_trade
packing_module_creation.png

Configure the Packing List

Go in portal_types tool, and modify the portal type Packing List like this:

  • Product meta type: ERP5 Delivery
  • Product factory method: addDelivery

When creating, add delivery to the Groups, in order to make acquisition between line and order.

Configure Packing List Module

We can now filter the content types in the Packing List Module. Check Filter content types?, and select Packing List in Allowed content types on the Packing List Module's Portal Type.

Create the Packing List Line portal type

Create the portal type Packing List Line, with default type information: ERP5Type: ERP5 Delivery Line. When creating, add delivery_movement to the Groups, in order to make acquisition between line and simulation movement.

packing_list_line_creation.png

Configure the forms

As for the Order and Order Line, create identical PackingList_view and PackingListLine_view, and associate them as the default view of there related Portal Type.

On PackingList_view, you just need to modify:

  • my_translated_simulation_state_title to my_translated_causality_state_title

Add packing_list_causality_workflow

First of all, associate edit_workflow to the Packing List Portal Type.

Then, we need to create manually the packing_list_causality_workflow. This workflow will be used to detect inconsistency between prevision and decision on the Packing List level. Add it in portal_workflow. Use the state variable name: causality_state. Currently, set the default state to solved.

packing_module_creation.png

Configure the Packing List Builder

In order to create automatically Packing List from the simulation, we need to create and configure a Delivery Builder. Go to portal_deliveries, and create a new Delivery Builder, with those attributes:

  • ID: packing_list_builder
  • title: Packing List Builder
  • delivery module: Packing Lists
  • delivery portal type: Packing List
  • delivery line portal type: Packing List Line
  • delivery cell portal type: Packing List Cell

Configure the Simulation Select Method

Create the script which will get the unassociated Simulation Movement. Add a python script 'Order_selectMovement' (with the parameter **kw) in the skin folder basic_trade.

Toggle line numbers
   1 # This script is only for tutorial purpose and must not be used 
   2 # on a production system
   3 result_list = []
   4 # Get applied rule related to the order rule 1
   5 applied_rule_list = [x for x in \
   6                      context.portal_simulation.contentValues() \
   7                      if x.getSpecialise() == 'portal_rules/1']
   8 # Find simulation movement not associated to a packing list
   9 for applied_rule in applied_rule_list:
  10   for simulation_movement in applied_rule.contentValues():
  11     if (simulation_movement.getPortalType() == \
  12                                    'Simulation Movement') and \
  13        (simulation_movement.getSimulationState() == 'confirmed') and \
  14        (simulation_movement.getDelivery() == None):
  15       # We found a matching movement
  16       result_list.append(simulation_movement)
  17 
  18 return result_list
Toggle line numbers # This script is only for tutorial purpose and must not be used # on a production system result_list = [] # Get applied rule related to the order rule 1 applied_rule_list = [x for x in \ context.portal_simulation.contentValues() \ if x.getSpecialise() == 'portal_rules/1'] # Find simulation movement not associated to a packing list for applied_rule in applied_rule_list: for simulation_movement in applied_rule.contentValues(): if (simulation_movement.getPortalType() == \ 'Simulation Movement') and \ (simulation_movement.getSimulationState() == 'confirmed') and \ (simulation_movement.getDelivery() == None): # We found a matching movement result_list.append(simulation_movement) return result_list

When created, set Order_selectMovement as Simulation Select Method on the delivery builder.

Configure the Order Groups

After getting the simulation movements that we need to aggregate, we have to group them by attribute values.

First of all, we will define which kind of movement we want to group on the same packing list. In Delivery Collect Order Groups, add those values:

  • OrderMovementGroup, which group the movements generated by the same Order, and set the causality on the Packing List created.
  • DateMovementGroup, which sets the dates on the Packing List.
  • PathMovementGroup, which sets the source and destination on the Packing List

Then, we will define how we group movements on the same line. Define in Delivery Line Collect Order Groups those values:

  • ResourceMovementGroup, which group the movements which have the same Resource.

Finally, define also ResourceMovementGroup in Delivery Cell Collect Order Groups (but we will not generate cells in our example).

packing_list_line_creation.png

Configure order_workflow

Time to configure the order_workflow, in order to make it create the packing list as soon as we change the state to confirmed. Add a workflow script called Order_createPackingList in order_workflow.

Give to this script the parameters: state_change, **kw.

Then, add this code:

Toggle line numbers
   1 order = state_change['object']
   2 # The ID of the rule in portal_rules we want to use.
   3 order_rule_id = '1'
   4 # Update the applied rule in activity.
   5 tag = order.getPath() + '_firstUpdateAppliedRule'
   6 activate_kw = {'tag':tag, 'priority':3}
   7 order.activate(tag=tag).updateAppliedRule(rule_id = order_rule_id,
   8                                           activate_kw=activate_kw)
   9 
  10 # Call build on the delivery builder
  11 delivery_builder = order.portal_deliveries.packing_list_builder
  12 delivery_builder.activate(activity='SQLQueue', 
  13                           after_tag=tag).build()
delivery_builder.png Toggle line numbers order = state_change['object'] # The ID of the rule in portal_rules we want to use. order_rule_id = '1' # Update the applied rule in activity. tag = order.getPath() + '_firstUpdateAppliedRule' activate_kw = {'tag':tag, 'priority':3} order.activate(tag=tag).updateAppliedRule(rule_id = order_rule_id, activate_kw=activate_kw) # Call build on the delivery builder delivery_builder = order.portal_deliveries.packing_list_builder delivery_builder.activate(activity='SQLQueue', after_tag=tag).build()

Finally, set Order_createPackingList as the Script (after) of the confirm transition.

Change the state of order 000001

Modify the state from planned to confirmed. After a few seconds, click on the Metadata tab of the Order. You will see /erp5/packing_list_module/1 as a related object of the order.

packing_list_causality_workflow_draft.png

Check the Packing List

In packing_list_module, you can see the created Packing List, with the same values as the Order. The causality state is solved.

packing_list_created.png

Step Five: Dealing with the Causality (outdated)

It's time for us to improve our causality workflow, in order to detect modification made by the user on the packing list, and to help the user to solve the problems.

Improve packing_list_causality_workflow

We will design the causality workflow in a simple way. It will be a 2 states workflow:

  • solved, when Packing List and Simulation are identical
  • diverged, when there is a difference

Add 2 Workflow Methods in the workflow:

  • converge, which modify the state to solved
  • diverge, which modify the state to diverged

Both of them can be called from solved and diverged states.

Picture Missing: packing_list_causality_workflow_version1.png

Create packing_list_interaction_workflow

In order to call the workflow methods converge and diverge automatically when we modify the packing list, we need to create an interaction workflow.

In portal_workflow, add a new interaction_workflow with the name packing_list_interaction_workflow, and associate it to the portal types Packing List and Packing List Line.

create_packing_list_interaction_workflow.png

In packing_list_interaction_workflow, create 2 python scripts with state_change and **kw as arguments.

  • PackingList_modifyCausality
  • Toggle line numbers
       1 packing_list = state_change['object']
       2 
       3 packing_list.updateCausalityState()
    
    Toggle line numbers packing_list = state_change['object'] packing_list.updateCausalityState()
  • PackingListLine_modifyCausality
  • Toggle line numbers
       1 line = state_change['object']
       2 
       3 line.getParentValue().updateCausalityState()
    
    Toggle line numbers line = state_change['object'] line.getParentValue().updateCausalityState()

Then, we will add 2 interactions with the trigger method id edit and _edit:

  • PackingList_edit, filtered with Packing List, calling PackingList_modifyCausality as Script (after)
  • PackingListLine_edit, filtered with Packing List Line, calling PackingListLine_modifyCausality as Script (after)
packing_list_interaction_workflow_interactions.png

Now, restart the site in order to generate all workflow methods on the Portal Type.

Adding a Divergence Tester

In order to precise on which property(ies) controls are done, you will have to add a divergence tester on your Order Rule. To achieve this, add by instance an Allowed Content Type = "Quantity Divergence Tester" for your portal type Order Rule if you want to test quantities on your Packing List Lines. Then go to your rule "portal_rules/1" and add an instance of this Quantity Divergence Tester.

Testing on our Packing List

It's time to test our workflows.

Go to the packing_list_module/1. Modify the quantity on the line (for example, change 10 to 9). You will see that causality state is now diverged.

packing_list_diverged.png

If you change again the value to 10, the causality state will be back to Solved(converged).

Our system is now able to detect divergency, but, can not resolve it manually, when we want to impact the simulation with the values entered on the packing list.

Step Six: Solving the divergency

Modify packing_list_causality_workflow

In packing_list_causality_workflow, add a Python script called PackingList_acceptDecision, with those parameters: state_change, **kw.

Toggle line numbers
   1 packing_list = state_change['object']
   2 
   3 # Make sure applied rule is 100% indexed
   4 # Here, applied rule must be already cleanly created
   5 
   6 # We do not use DeliverySolver, because, by default, 
   7 # we keep delivery_ratio untouch
   8 
   9 # CopyToTarget is a TargetSolver which copy 
  10 # the PackingListLine quantity to the Simulation Movement Quantity
  11 packing_list.portal_simulation.solveDelivery(packing_list, None,
  12                                              "CopyToTarget")
  13 
  14 # Automatic workflow
  15 packing_list.activate().updateCausalityState()
Toggle line numbers packing_list = state_change['object'] # Make sure applied rule is 100% indexed # Here, applied rule must be already cleanly created # We do not use DeliverySolver, because, by default, # we keep delivery_ratio untouch # CopyToTarget is a TargetSolver which copy # the PackingListLine quantity to the Simulation Movement Quantity packing_list.portal_simulation.solveDelivery(packing_list, None, "CopyToTarget") # Automatic workflow packing_list.activate().updateCausalityState()

Then, add 2 new transitions in this workflow:

  • accept_decision, which is a WorkflowMethod, calling the Python Script PackingList_acceptDecision in the Script (after)
  • accept_decision_action, which is a User action, calling accept_decision in the Script (after) with Name as Accept Decision and URL as and URL as '%(content_url)s/?BaseWorkflow_viewWorkflowActionDialog?workflow_action=accept_decision_action

Both of those transitions can be called when we are in the diverged state.

packing_list_causality_workflow_transitions.png packing_list_causality_workflow_version2.png

As usual, restart the site for generating the Workflow Methods.

Testing on the Packing List

On our packing list, modify the quantity line to 9. The causality state will be again diverged.

Now, you will see Accept Decision as a available workflow transition in the Action Menu.

packing_list_solving_action1.png

Select this action, and after a few second, you will see that even if the quantity is still 9, the causality state of the packing list is now solved.

packing_list_solved.png

Checking the Simulation Movement

If you now look at the simulation movement portal_simulation/1/1, you will see that the quantity is now 9, with the action of our Solver.

packing_list_solved.png

Related Articles