diff --git a/product/ERP5/DeliverySolver/FIFO.py b/product/ERP5/DeliverySolver/FIFO.py index cdbe08ae55c3e207e2c2a5ba10cb883367fe71e2..d18f961a78e0527a38ec21b70c6a932261986c00 100644 --- a/product/ERP5/DeliverySolver/FIFO.py +++ b/product/ERP5/DeliverySolver/FIFO.py @@ -33,3 +33,30 @@ class FIFO(DeliverySolver): """ The FIFO solver reduces deliveted quantity by reducing the quantity of simulation movements from the last order. """ + + def solve(self, simulation_movement_list, new_quantity): + """ + """ + result = [] + def sortByOrderStartDate(a, b); + return cmp(a.getExplainationValue().getStartDate() b.getExplainationValue().getStartDate()) + simulation_movement_list.sort(sortByOrderStartDate) + simulation_movement_list.reverse() + total_quantity = 0 + for movement in simulation_movement_list: + total_quantity += movement.getQuantity() + remaining_quantity = total_quantity - new_quantity + for movement in simulation_movement_list: + if remaining_quantity: + if movement.getQuantity() < remaining_quantity: + result.append((movement, movement.getQuantity())) + remaining_quantity -= movement.getQuantity() + movement.setQuantity(0) + else: + result.append((movement, remaining_quantity)) + movement.setQuantity(movement.getQuantity() - remaining_quantity) + remaining_quantity = 0 + # Return movement, split_quantity tuples + for movement in simulation_movement_list: + movement.setDeliveryRatio(movement.getQuantity() / new_quantity) + return result \ No newline at end of file diff --git a/product/ERP5/Document/AcceptSolver.py b/product/ERP5/Document/AcceptSolver.py index fc0c22b7c70481958b946eb215d227228012492b..0c0c103092ab79473ebcab885a3524db0756ee36 100644 --- a/product/ERP5/Document/AcceptSolver.py +++ b/product/ERP5/Document/AcceptSolver.py @@ -52,4 +52,19 @@ class AcceptSolver(XMLObject): , PropertySheet.XMLObject , PropertySheet.CategoryCore , PropertySheet.DublinCore - ) \ No newline at end of file + ) + + # Implementation + def solve(self): + """ + """ + # Adopt new property, keep the original one recorded + solved_property = self.getPortalTypeValue().getTestedProperty() + for movement in self.getDeliveryValueList(): + new_value = movement.getProperty(solved_property) + for simulation_movement in movement.getDeliveryRelatedValueList(): + if not simulation_movement.isPropertyRecorded(solved_property): + simulation_movement.recordProperty(solved_property) + solved_property.setProperty(solved_property, new_value) + # Finish solving + self.succeed() \ No newline at end of file diff --git a/product/ERP5/Document/QuantitySplitSolver.py b/product/ERP5/Document/QuantitySplitSolver.py index 0f394a51c18e10593d8337a825b5a13edb69b69c..5c30a65abc88f41a1f0f121cf6907b3a84333eb7 100644 --- a/product/ERP5/Document/QuantitySplitSolver.py +++ b/product/ERP5/Document/QuantitySplitSolver.py @@ -55,3 +55,19 @@ class QuantitySplitSolver(XMLObject): , PropertySheet.DublinCore , PropertySheet.Arrow ) + # Implementation + def solve(self): + """ + """ + delivery_solver = self.portal_solvers.buildDeliverySolver(self.getDeliverySolver()) + for delivery_line in self.getDeliveryValueList(): + decision_quantity = delivery_line.getQuantity() + simulation_movement_list = self.getDeliveryRelatedValueList() + # Update the quantity using delivery solver algorithm + split_list = delivery_solver.solve(simulation_movement_list, decision_quantity) + # Create split movements + for (simulation_movement, split_quantity) in split_list: + new_movement = simulation_movement.copy() # Copy at same level + new_movement._setQuantity(split_quantity) + new_movement._setStartDate(self.getStartDate()) + new_movement._setStopDate(self.getStopDate()) \ No newline at end of file diff --git a/product/ERP5/Document/SolverProcess.py b/product/ERP5/Document/SolverProcess.py index 5c3bcf73e49ee610a11562dd33181c195b5466f5..1a172b4433a6e6844c523beb260d240ba769a924 100644 --- a/product/ERP5/Document/SolverProcess.py +++ b/product/ERP5/Document/SolverProcess.py @@ -71,4 +71,62 @@ class SolverProcess(XMLObject, ActiveProcess): , PropertySheet.XMLObject , PropertySheet.CategoryCore , PropertySheet.DublinCore - ) \ No newline at end of file + ) + + def buildTargetSolverList(self): + """ + Builds target solvers from solver decisions + """ + solver_dict = {} + movement_dict = {} + types_tool = context.portal_types + + # First create a mapping between delivery movements and solvers + # in order to know for each movements which solvers are needed + # and which parameters with + for decision in context.contentValues(portal_type="Solver Decision"): + solver = decision.getSolverValue() + solver_type = solver.getId() # ex. Postpone Production Solver + solver_conviguration_dict = decision.getConfigurationPropertyDict() + solver_conviguration_key = solver_conviguration_dict.items() + for movement in decision.getDeliveryValueList(): + # Detect incompatibilities + movement_solver_dict = movement_dict.setdefault(movement.getRelativeUrl(), {}) + movement_solver_configuration_dict = movement_solver_dict.setdefault(solver_type, {}) + movement_solver_configuration_dict[solver_key] = None + + # Second, make sure solvers do not conflict and configuration is valid + for movement_url, movement_solver_dict in movement_dict.items(): + for solver_type, movement_solver_configuration_dict in movement_solver_dict.items(): + solver = types_tool[solver_type] + for other_solver in movement_solver_dict.keys(): + if solver.conflictsWithSolver(other_solver): + raise "Solver %s conflicts with solver %s on movement %s" % (solver_type, other_solver, movement_url) + # Make sure multiple configuration are possible + try: + # Solver key contains only those properties which differentiate + # solvers (ex. there should be only Production Reduction Solver) + solver_key = solver.reduceConfigurationList(movement_solver_configuration_dict.keys()) + except: + raise + solver_key_dict = solver_dict.setdefault(solver_type, {}) + solver_movement_dict = solver_key_dict.setdefault(solver_key, {}) + solver_movement_dict[movement_url] = movement_solver_configuration_dict.keys() + + # Third, build target solvers + for portal_type, solver_key_dict in solver_dict.items(): + for solver_key, solver_movement_dict in solver_key_dict.items(): + solver_instance = self.newContent(portal_type=solver_type) + solver_instance._setDeliveryList(solver_movement_dict.keys()) + for movement_url, configuration_list: + for configuration_kw in configuration_list: + solver_instance.updateConfiguration(**configuration_kw) + + # Solver Process Workflow Interface + # NOTE: how can we consider that a workflow defines or provides an interface ? + def solve(self): + """ + Start solving + """ + for solver in self.contentValues(portal_type=self.getPortalObject().getPortalTargetSolverTypeList()): + solver.activate(active_process=self).solve() \ No newline at end of file