001: /*
002: * ProductNode.java
003: *
004: * Created on 1 ottobre 2003, 16.10
005: */
006:
007: package org.ofbiz.manufacturing.bom;
008:
009: import java.util.Map;
010: import java.util.List;
011: import java.util.ArrayList;
012: import java.util.HashMap;
013: import java.util.Iterator;
014: import java.util.Date;
015:
016: import org.ofbiz.entity.util.EntityUtil;
017: import org.ofbiz.entity.GenericDelegator;
018: import org.ofbiz.entity.GenericValue;
019: import org.ofbiz.entity.GenericEntityException;
020: import org.ofbiz.base.util.UtilMisc;
021:
022: /** An ItemCoinfigurationNode represents a component in a bill of materials.
023: * @author <a href="mailto:tiz@sastau.it">Jacopo Cappellato</a>
024: */
025:
026: public class ItemConfigurationNode {
027:
028: private ItemConfigurationNode parentNode; // the parent node (null if it's not present)
029: private ItemConfigurationNode substitutedNode; // The virtual node (if any) that this instance substitutes
030: private GenericValue ruleApplied; // The rule (if any) that that has been applied to configure the current node
031: private String productForRules;
032: private GenericValue part; // the current product (from Product entity)
033: private ArrayList children; // current node's children (ProductAssocs)
034: private ArrayList childrenNodes; // current node's children nodes (ItemConfigurationNode)
035: private float quantityMultiplier; // the necessary quantity as declared in the bom (from ProductAssocs or ProductManufacturingRule)
036: // Runtime fields
037: private int depth; // the depth of this node in the current tree
038: private float quantity; // the quantity of this node in the current tree
039: private String bomTypeId; // the type of the current tree
040:
041: public ItemConfigurationNode(GenericValue part) {
042: this .part = part;
043: children = new ArrayList();
044: childrenNodes = new ArrayList();
045: parentNode = null;
046: productForRules = null;
047: bomTypeId = null;
048: quantityMultiplier = 1;
049: // Now we initialize the fields used in breakdowns
050: depth = 0;
051: quantity = 0;
052: }
053:
054: public ItemConfigurationNode(String partId,
055: GenericDelegator delegator) throws GenericEntityException {
056: this (delegator.findByPrimaryKey("Product", UtilMisc.toMap(
057: "productId", partId)));
058: }
059:
060: protected void loadChildren(String partBomTypeId, Date inDate,
061: List productFeatures) throws GenericEntityException {
062: if (part == null) {
063: throw new GenericEntityException("Part is null");
064: }
065: // If the date is null, set it to today.
066: if (inDate == null)
067: inDate = new Date();
068:
069: bomTypeId = partBomTypeId;
070: GenericDelegator delegator = part.getDelegator();
071: List rows = delegator.findByAnd("ProductAssoc", UtilMisc.toMap(
072: "productId", part.get("productId"),
073: "productAssocTypeId", partBomTypeId), UtilMisc
074: .toList("sequenceNum"));
075: rows = EntityUtil.filterByDate(rows, inDate);
076: if ((rows == null || rows.size() == 0)
077: && substitutedNode != null) {
078: // If no child is found and this is a substituted node
079: // we try to search for substituted node's children.
080: rows = delegator.findByAnd("ProductAssoc", UtilMisc.toMap(
081: "productId", substitutedNode.getPart().get(
082: "productId"), "productAssocTypeId",
083: partBomTypeId), UtilMisc.toList("sequenceNum"));
084: rows = EntityUtil.filterByDate(rows, inDate);
085: }
086: children = new ArrayList(rows);
087: childrenNodes = new ArrayList();
088: Iterator childrenIterator = children.iterator();
089: GenericValue oneChild = null;
090: ItemConfigurationNode oneChildNode = null;
091: while (childrenIterator.hasNext()) {
092: oneChild = (GenericValue) childrenIterator.next();
093: // Configurator
094: oneChildNode = configurator(oneChild, productFeatures,
095: getRootNode().getProductForRules(), inDate,
096: delegator);
097: // If the node is null this means that the node has been discarded by the rules.
098: if (oneChildNode != null) {
099: oneChildNode.setParentNode(this );
100: oneChildNode.loadChildren(partBomTypeId, inDate,
101: productFeatures);
102: }
103: childrenNodes.add(oneChildNode);
104: }
105: }
106:
107: private ItemConfigurationNode substituteNode(
108: ItemConfigurationNode oneChildNode, List productFeatures,
109: List productPartRules, GenericDelegator delegator)
110: throws GenericEntityException {
111: if (productPartRules != null) {
112: GenericValue rule = null;
113: for (int i = 0; i < productPartRules.size(); i++) {
114: rule = (GenericValue) productPartRules.get(i);
115: String ruleCondition = (String) rule
116: .get("productFeature");
117: String ruleOperator = (String) rule.get("ruleOperator");
118: String newPart = (String) rule.get("productIdInSubst");
119: float ruleQuantity = 0;
120: try {
121: ruleQuantity = rule.getDouble("quantity")
122: .floatValue();
123: } catch (Exception exc) {
124: ruleQuantity = 0;
125: }
126:
127: GenericValue feature = null;
128: boolean ruleSatisfied = false;
129: if (ruleCondition == null || ruleCondition.equals("")) {
130: ruleSatisfied = true;
131: } else {
132: if (productFeatures != null) {
133: for (int j = 0; j < productFeatures.size(); j++) {
134: feature = (GenericValue) productFeatures
135: .get(j);
136: if (ruleCondition.equals((String) feature
137: .get("productFeatureId"))) {
138: ruleSatisfied = true;
139: break;
140: }
141: }
142: }
143: }
144: if (ruleSatisfied && ruleOperator.equals("OR")) {
145: ItemConfigurationNode tmpNode = oneChildNode;
146: if (newPart == null || newPart.equals("")) {
147: oneChildNode = null;
148: } else {
149: oneChildNode = new ItemConfigurationNode(
150: newPart, delegator);
151: oneChildNode.setSubstitutedNode(tmpNode);
152: oneChildNode.setRuleApplied(rule);
153: if (ruleQuantity > 0) {
154: oneChildNode
155: .setQuantityMultiplier(ruleQuantity);
156: }
157: }
158: break;
159: }
160: // FIXME: AND operator still not implemented
161: } // end of for
162:
163: }
164: return oneChildNode;
165: }
166:
167: private ItemConfigurationNode configurator(GenericValue node,
168: List productFeatures, String productIdForRules,
169: Date inDate, GenericDelegator delegator)
170: throws GenericEntityException {
171: ItemConfigurationNode oneChildNode = new ItemConfigurationNode(
172: (String) node.get("productIdTo"), delegator);
173: try {
174: oneChildNode.setQuantityMultiplier(node.getDouble(
175: "quantity").floatValue());
176: } catch (NumberFormatException nfe) {
177: oneChildNode.setQuantityMultiplier(1);
178: }
179: ItemConfigurationNode newNode = oneChildNode;
180: // CONFIGURATOR
181: if (oneChildNode.isVirtual()) {
182: // If the part is VIRTUAL and
183: // productFeatures and productPartRules are not null
184: // we have to substitute the part with the right part's variant
185: List productPartRules = delegator.findByAnd(
186: "ProductManufacturingRule", UtilMisc.toMap(
187: "productId", productIdForRules,
188: "productIdFor", node.get("productId"),
189: "productIdIn", node.get("productIdTo")));
190: if (substitutedNode != null) {
191: productPartRules.addAll(delegator
192: .findByAnd("ProductManufacturingRule", UtilMisc
193: .toMap("productId", productIdForRules,
194: "productIdFor", substitutedNode
195: .getPart().getString(
196: "productId"),
197: "productIdIn", node
198: .get("productIdTo"))));
199: }
200: productPartRules = EntityUtil.filterByDate(
201: productPartRules, inDate);
202: newNode = substituteNode(oneChildNode, productFeatures,
203: productPartRules, delegator);
204: if (newNode == oneChildNode) {
205: // If no substitution has been done (no valid rule applied),
206: // we try to search for a generic link-rule
207: List genericLinkRules = delegator
208: .findByAnd("ProductManufacturingRule", UtilMisc
209: .toMap("productIdFor", node
210: .get("productId"),
211: "productIdIn", node
212: .get("productIdTo")));
213: if (substitutedNode != null) {
214: genericLinkRules.addAll(delegator.findByAnd(
215: "ProductManufacturingRule", UtilMisc.toMap(
216: "productIdFor", substitutedNode
217: .getPart().getString(
218: "productId"),
219: "productIdIn", node
220: .get("productIdTo"))));
221: }
222: genericLinkRules = EntityUtil.filterByDate(
223: genericLinkRules, inDate);
224: newNode = null;
225: newNode = substituteNode(oneChildNode, productFeatures,
226: genericLinkRules, delegator);
227: if (newNode == oneChildNode) {
228: // If no substitution has been done (no valid rule applied),
229: // we try to search for a generic node-rule
230: List genericNodeRules = delegator.findByAnd(
231: "ProductManufacturingRule", UtilMisc.toMap(
232: "productIdIn", node
233: .get("productIdTo")),
234: UtilMisc.toList("ruleSeqId"));
235: genericNodeRules = EntityUtil.filterByDate(
236: genericNodeRules, inDate);
237: newNode = null;
238: newNode = substituteNode(oneChildNode,
239: productFeatures, genericNodeRules,
240: delegator);
241: if (newNode == oneChildNode) {
242: // If no substitution has been done (no valid rule applied),
243: // we try to set the default (first) node-substitution
244: if (genericNodeRules != null
245: && genericNodeRules.size() > 0) {
246: // FIXME
247: //...
248: }
249: }
250: }
251: }
252: } // end of if (isVirtual())
253: return newNode;
254: }
255:
256: protected void loadParents(String partBomTypeId, Date inDate,
257: List productFeatures) throws GenericEntityException {
258: if (part == null) {
259: throw new GenericEntityException("Part is null");
260: }
261: // If the date is null, set it to today.
262: if (inDate == null)
263: inDate = new Date();
264:
265: bomTypeId = partBomTypeId;
266: GenericDelegator delegator = part.getDelegator();
267: List rows = delegator.findByAnd("ProductAssoc", UtilMisc.toMap(
268: "productIdTo", part.get("productId"),
269: "productAssocTypeId", partBomTypeId), UtilMisc
270: .toList("sequenceNum"));
271: rows = EntityUtil.filterByDate(rows, inDate);
272: if ((rows == null || rows.size() == 0)
273: && substitutedNode != null) {
274: // If no parent is found and this is a substituted node
275: // we try to search for substituted node's parents.
276: rows = delegator.findByAnd("ProductAssoc", UtilMisc.toMap(
277: "productIdTo", substitutedNode.getPart().get(
278: "productId"), "productAssocTypeId",
279: partBomTypeId), UtilMisc.toList("sequenceNum"));
280: rows = EntityUtil.filterByDate(rows, inDate);
281: }
282: children = new ArrayList(rows);
283: childrenNodes = new ArrayList();
284: Iterator childrenIterator = children.iterator();
285: GenericValue oneChild = null;
286: ItemConfigurationNode oneChildNode = null;
287: while (childrenIterator.hasNext()) {
288: oneChild = (GenericValue) childrenIterator.next();
289: oneChildNode = new ItemConfigurationNode(oneChild
290: .getString("productId"), delegator);
291: // Configurator
292: //oneChildNode = configurator(oneChild, productFeatures, getRootNode().getProductForRules(), delegator);
293: // If the node is null this means that the node has been discarded by the rules.
294: if (oneChildNode != null) {
295: oneChildNode.setParentNode(this );
296: oneChildNode.loadParents(partBomTypeId, inDate,
297: productFeatures);
298: }
299: childrenNodes.add(oneChildNode);
300: }
301: }
302:
303: /** Getter for property parentNode.
304: * @return Value of property parentNode.
305: *
306: */
307: public ItemConfigurationNode getParentNode() {
308: return parentNode;
309: }
310:
311: public ItemConfigurationNode getRootNode() {
312: return (parentNode != null ? getParentNode() : this );
313: }
314:
315: /** Setter for property parentNode.
316: * @param parentNode New value of property parentNode.
317: *
318: */
319: public void setParentNode(ItemConfigurationNode parentNode) {
320: this .parentNode = parentNode;
321: }
322:
323: // ------------------------------------
324: // Method used for TEST and DEBUG purposes
325: public void print(StringBuffer sb, float quantity, int depth) {
326: for (int i = 0; i < depth; i++) {
327: sb.append("<b> * </b>");
328: }
329: sb.append(part.get("productId"));
330: sb.append(" - ");
331: sb.append("" + quantity);
332: GenericValue oneChild = null;
333: ItemConfigurationNode oneChildNode = null;
334: depth++;
335: for (int i = 0; i < children.size(); i++) {
336: oneChild = (GenericValue) children.get(i);
337: float bomQuantity = 0;
338: try {
339: bomQuantity = oneChild.getDouble("quantity")
340: .floatValue();
341: } catch (Exception exc) {
342: bomQuantity = 1;
343: }
344: oneChildNode = (ItemConfigurationNode) childrenNodes.get(i);
345: sb.append("<br>");
346: if (oneChildNode != null) {
347: oneChildNode.print(sb, (quantity * bomQuantity), depth);
348: }
349: }
350: }
351:
352: public void print(ArrayList arr, float quantity, int depth) {
353: // Now we set the depth and quantity of the current node
354: // in this breakdown.
355: this .depth = depth;
356: this .quantity = quantity * quantityMultiplier;
357: // First of all we visit the corrent node.
358: arr.add(this );
359: // Now (recursively) we visit the children.
360: GenericValue oneChild = null;
361: ItemConfigurationNode oneChildNode = null;
362: depth++;
363: for (int i = 0; i < children.size(); i++) {
364: oneChild = (GenericValue) children.get(i);
365: // float bomQuantity = 0;
366: // try {
367: // bomQuantity = oneChild.getDouble("quantity").floatValue();
368: // } catch(Exception exc) {
369: // bomQuantity = 1;
370: // }
371: oneChildNode = (ItemConfigurationNode) childrenNodes.get(i);
372: if (oneChildNode != null) {
373: // oneChildNode.print(arr, (quantity * bomQuantity), depth);
374: oneChildNode.print(arr, this .quantity, depth);
375: }
376: }
377: }
378:
379: // Method used for TEST and DEBUG purposes
380: public void sumQuantity(HashMap nodes) {
381: // First of all, we try to fetch a node with the same partId
382: ItemConfigurationNode sameNode = (ItemConfigurationNode) nodes
383: .get(part.getString("productId"));
384: // If the node is not found we create a new node for the current part
385: if (sameNode == null) {
386: sameNode = new ItemConfigurationNode(part);
387: nodes.put(part.getString("productId"), sameNode);
388: }
389: // Now we add the current quantity to the node
390: sameNode.setQuantity(sameNode.getQuantity() + quantity);
391: // Now (recursively) we visit the children.
392: ItemConfigurationNode oneChildNode = null;
393: for (int i = 0; i < childrenNodes.size(); i++) {
394: oneChildNode = (ItemConfigurationNode) childrenNodes.get(i);
395: if (oneChildNode != null) {
396: oneChildNode.sumQuantity(nodes);
397: }
398: }
399: }
400:
401: protected boolean isVirtual() {
402: return (part.get("isVirtual") != null ? part.get("isVirtual")
403: .equals("Y") : false);
404: }
405:
406: public void isConfigured(ArrayList arr) {
407: // First of all we visit the corrent node.
408: if (isVirtual()) {
409: arr.add(this );
410: }
411: // Now (recursively) we visit the children.
412: GenericValue oneChild = null;
413: ItemConfigurationNode oneChildNode = null;
414: for (int i = 0; i < children.size(); i++) {
415: oneChild = (GenericValue) children.get(i);
416: oneChildNode = (ItemConfigurationNode) childrenNodes.get(i);
417: if (oneChildNode != null) {
418: oneChildNode.isConfigured(arr);
419: }
420: }
421: }
422:
423: /** Getter for property quantity.
424: * @return Value of property quantity.
425: *
426: */
427: public float getQuantity() {
428: return quantity;
429: }
430:
431: public void setQuantity(float quantity) {
432: this .quantity = quantity;
433: }
434:
435: /** Getter for property depth.
436: * @return Value of property depth.
437: *
438: */
439:
440: public int getDepth() {
441: return depth;
442: }
443:
444: public GenericValue getPart() {
445: return part;
446: }
447:
448: /** Getter for property substitutedNode.
449: * @return Value of property substitutedNode.
450: *
451: */
452: public ItemConfigurationNode getSubstitutedNode() {
453: return substitutedNode;
454: }
455:
456: /** Setter for property substitutedNode.
457: * @param substitutedNode New value of property substitutedNode.
458: *
459: */
460: public void setSubstitutedNode(ItemConfigurationNode substitutedNode) {
461: this .substitutedNode = substitutedNode;
462: }
463:
464: public String getRootProductForRules() {
465: return getParentNode().getProductForRules();
466: }
467:
468: /** Getter for property productForRules.
469: * @return Value of property productForRules.
470: *
471: */
472: public String getProductForRules() {
473: return productForRules;
474: }
475:
476: /** Setter for property productForRules.
477: * @param productForRules New value of property productForRules.
478: *
479: */
480: public void setProductForRules(String productForRules) {
481: this .productForRules = productForRules;
482: }
483:
484: /** Getter for property bomTypeId.
485: * @return Value of property bomTypeId.
486: *
487: */
488: public java.lang.String getBomTypeId() {
489: return bomTypeId;
490: }
491:
492: /** Getter for property quantityMultiplier.
493: * @return Value of property quantityMultiplier.
494: *
495: */
496: public float getQuantityMultiplier() {
497: return quantityMultiplier;
498: }
499:
500: /** Setter for property quantityMultiplier.
501: * @param quantityMultiplier New value of property quantityMultiplier.
502: *
503: */
504: public void setQuantityMultiplier(float quantityMultiplier) {
505: this .quantityMultiplier = quantityMultiplier;
506: }
507:
508: /** Getter for property ruleApplied.
509: * @return Value of property ruleApplied.
510: *
511: */
512: public org.ofbiz.entity.GenericValue getRuleApplied() {
513: return ruleApplied;
514: }
515:
516: /** Setter for property ruleApplied.
517: * @param ruleApplied New value of property ruleApplied.
518: *
519: */
520: public void setRuleApplied(org.ofbiz.entity.GenericValue ruleApplied) {
521: this.ruleApplied = ruleApplied;
522: }
523:
524: }
|