feat: Enhance inventory and invoice processing

- Added functionality to handle M_Movement_ID in MID_MInventory, allowing for the creation of inventory lines based on movement lines.
- Implemented a method to sum movement lines in MID_MInventory.
- Improved MID_MInventoryLineMA to ensure proper handling of movement quantities and attribute set instances.
- Introduced createLineFrom method in MID_MInvoice to facilitate line creation from various sources (order, shipment, RMA).
- Updated MID_MPayment to set prepayment status based on order presence.
- Enhanced MID_Production with methods for reversing production documents and validating production lines.
- Created MID_ProductionPlan class to manage production plans and their associated lines.
- Developed MID_InvoiceCreateLineFrom process to automate invoice line creation from selections.
This commit is contained in:
faisolavolut 2025-09-02 10:34:42 +07:00
parent e47a1fd6f1
commit 2292c43d50
10 changed files with 1026 additions and 44 deletions

View File

@ -43,6 +43,15 @@ public class MID_CalloutOrder extends CalloutEngine implements IColumnCallout {
if(!isSOTrx) {
log.info("Setting PaymentRule to OnCredit for non-SOTrx");
mTab.setValue("PaymentRule", X_C_Order.PAYMENTRULE_OnCredit);
}else {
int org = (int) mTab.getValue(MOrder.COLUMNNAME_AD_Org_ID);
if (org == 1000002) { // Balinusa
log.info("Setting PaymentRule to Cash for SOTrx in Balinusa");
mTab.setValue("PaymentRule", X_C_Order.PAYMENTRULE_Cash);
} else { // MidSuit
log.info("Setting PaymentRule to OnCredit for SOTrx in MidSuit");
mTab.setValue("PaymentRule", X_C_Order.PAYMENTRULE_OnCredit);
}
}
}
}
@ -96,7 +105,15 @@ public class MID_CalloutOrder extends CalloutEngine implements IColumnCallout {
.setParameters(new Object[] { org, isSOTrx })
.setOnlyActiveRecords(true)
.firstId();
if(isSOTrx) {
if (org == 1000002) { // Balinusa
log.info("Setting PaymentRule to Cash for SOTrx in Balinusa");
mTab.setValue("PaymentRule", X_C_Order.PAYMENTRULE_Cash);
} else { // MidSuit
log.info("Setting PaymentRule to OnCredit for SOTrx in MidSuit");
mTab.setValue("PaymentRule", X_C_Order.PAYMENTRULE_OnCredit);
}
}
if(priceList>0)
mTab.setValue(MOrder.COLUMNNAME_M_PriceList_ID, priceList);

View File

@ -19,14 +19,18 @@ public class MID_CalloutPPProductBOMLine extends CalloutEngine implements IColum
// TODO Auto-generated method stub
if(mField.getColumnName().equals("PriceEntered") || mField.getColumnName().equals("QtyBOM")) {
// set linenetamt = QtyUsed * PriceEntered
BigDecimal C_Charge_ID = BigDecimal.valueOf((int) mTab.getValue("C_Charge_ID"));
if (C_Charge_ID != null && C_Charge_ID.intValue() <= 0) {
mTab.setValue("LineNetAmt", null);
return null; // do not calculate LineNetAmt if C_Charge_ID is set
BigDecimal C_Charge_ID = null;
Object chargeValue = mTab.getValue("C_Charge_ID");
if (chargeValue != null) {
C_Charge_ID = BigDecimal.valueOf((int) chargeValue);
}
if (C_Charge_ID == null || C_Charge_ID.intValue() <= 0) {
mTab.setValue("LineNetAmt", null);
return null; // do not calculate LineNetAmt if C_Charge_ID is not set
}
// if product is null
int M_Product_ID = (int) mTab.getValue("M_Product_ID");
if (M_Product_ID <= 0) {
BigDecimal M_Product_ID = (BigDecimal) mTab.getValue("M_Product_ID");
if (M_Product_ID == null || M_Product_ID.intValue() <= 0) {
mTab.setValue("LineNetAmt", null);
return null; // do not calculate LineNetAmt if M_Product_ID is not set
}

View File

@ -135,11 +135,11 @@ public class MID_DocProduction extends Doc
sqlPL = "SELECT * FROM M_ProductionLine pro_line "
+ "INNER JOIN M_ProductionPlan plan ON pro_line.M_ProductionPlan_id = plan.M_ProductionPlan_id "
+ "INNER JOIN M_Production pro ON pro.M_Production_id = plan.M_Production_id "
+ "WHERE pro.M_Production_ID=? "
+ "WHERE (pro_line.m_product_id is not null or pro_line.c_charge_id is not null ) and pro.M_Production_ID=? "
+ "ORDER BY plan.M_ProductionPlan_id, pro_line.Line";
} else {
sqlPL = "SELECT * FROM M_ProductionLine pl "
+ "WHERE pl.M_Production_ID=? "
+ "WHERE (pl.m_product_id is not null or pl.c_charge_id is not null ) and pl.M_Production_ID=? "
+ "ORDER BY pl.Line";
}
@ -207,6 +207,16 @@ public class MID_DocProduction extends Doc
BigDecimal retValue = Env.ZERO;
return retValue;
} // getBalance
public boolean isEndProductWithAnyLinesChargeAndNoMaterial(X_M_Production prod) {
String sql = "SELECT COUNT(*) FROM M_ProductionLine WHERE M_Production_ID=? AND C_Charge_ID IS NOT NULL AND M_Product_ID IS NULL";
int count = DB.getSQLValue(getTrxName(), sql, prod.getM_Production_ID());
String sql2 = "SELECT COUNT(*) FROM M_ProductionLine WHERE M_Production_ID=? AND C_Charge_ID IS NULL AND M_Product_ID IS NOT NULL AND IsEndProduct='N'";
int count2 = DB.getSQLValue(getTrxName(), sql2, prod.getM_Production_ID());
if (count > 0 && count2 == 0)
return true;
return false;
}
/**
* Create Facts (the accounting logic) for
@ -230,6 +240,7 @@ public class MID_DocProduction extends Doc
X_M_Production prod = (X_M_Production)getPO();
HashMap<String, BigDecimal> costMap = new HashMap<String, BigDecimal>();
BigDecimal totalNetAmt = BigDecimal.ZERO;
boolean isEndProductWithAnyLinesChargeAndNoMaterial = isEndProductWithAnyLinesChargeAndNoMaterial(prod);
for (int i = 0; i < p_lines.length; i++)
{
DocLine line = p_lines[i];
@ -240,7 +251,7 @@ public class MID_DocProduction extends Doc
if (!line.isItem()) {
// Handle CHARGE (tanpa product)
log.info("Handling Charge for ProductionLine: " + prodline.getLine() + " - " + prodline.getDescription());
log.info("[1] Handling Charge for ProductionLine: " + prodline.getLine() + " - " + prodline.getDescription());
int C_Charge_ID = prodline.get_ValueAsInt("C_Charge_ID");
BigDecimal LineNetAmt = (BigDecimal) prodline.get_Value("LineNetAmt");
@ -276,11 +287,11 @@ public class MID_DocProduction extends Doc
MProductionLineMA mas[] = MProductionLineMA.get(getCtx(), prodline.get_ID(), getTrxName());
MProduct product = (MProduct) prodline.getM_Product();
log.info("Processing ProductionLine: " + prodline.getLine() + " - " + prodline.getDescription()
log.info("[2] Processing ProductionLine: " + prodline.getLine() + " - " + prodline.getDescription()
+ ", Product=" + product.getName() + " - " + product.getValue());
String CostingLevel = product.getCostingLevel(as);
String costingMethod = product.getCostingMethod(as);
log.info("CostingLevel=" + CostingLevel + ", CostingMethod=" + costingMethod
log.info("[3] CostingLevel=" + CostingLevel + ", CostingMethod=" + costingMethod
+ ", Product=" + product.getName() + " - " + product.getValue());
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel)) {
@ -325,10 +336,10 @@ public class MID_DocProduction extends Doc
int stdPrecision = as.getStdPrecision();
BigDecimal bomCost = Env.ZERO;
BigDecimal qtyProduced = null;
log.info("Costs: " + costs + ", Is BOM:" + line.isProductionBOM() +", CostingLevel=" + CostingLevel
log.info("[4] Costs: " + costs + ", Is BOM:" + line.isProductionBOM() +", CostingLevel=" + CostingLevel
+ ", CostingMethod=" + costingMethod + ", Product=" + product.getName() + " - " + product.getValue());
if (line.isProductionBOM()) {
log.info("Processing Production BOM Line: " + line.getLine() + " - " + line.getDescription()
log.info("[5] Processing Production BOM Line: " + line.getLine() + " - " + line.getDescription()
+ ", Product=" + product.getName() + " - " + product.getValue());
X_M_ProductionLine endProLine = (X_M_ProductionLine) line.getPO();
int parentEndPro = prod.isUseProductionPlan() ? endProLine.getM_ProductionPlan_ID() : endProLine.getM_Production_ID();
@ -341,8 +352,11 @@ public class MID_DocProduction extends Doc
if (bomProLine.getM_Product_ID() == 0) {
// add bomCost
BigDecimal LineNetAmt = (BigDecimal) bomProLine.get_Value("LineNetAmt");
if (LineNetAmt == null) {
LineNetAmt = Env.ZERO;
}
totalChargeAmt = totalChargeAmt.add(LineNetAmt);
log.info("Charge LineNetAmt: " + LineNetAmt + ", Total Charge Amount: " + totalChargeAmt);
log.info("[6] Charge LineNetAmt: " + LineNetAmt + ", Total Charge Amount: " + totalChargeAmt);
continue; // skip product logic for charge
}
int parentBomPro = prod.isUseProductionPlan() ? bomProLine.getM_ProductionPlan_ID() : bomProLine.getM_Production_ID();
@ -350,9 +364,9 @@ public class MID_DocProduction extends Doc
if (parentBomPro != parentEndPro)
continue;
if (!line0.isProductionBOM()) {
log.info("bomProLine ID: " + bomProLine.get_ID() + "");
log.info("[7] bomProLine ID: " + bomProLine.get_ID() + "");
MProduct product0 = (MProduct) bomProLine.getM_Product();
log.info("Processing BOM Line: " + bomProLine.getLine() + " - " + bomProLine.getDescription()
log.info("[8] Processing BOM Line: " + bomProLine.getLine() + " - " + bomProLine.getDescription()
+ ", Product=" + product0.getName() + " - " + product0.getValue());
String CostingLevel0 = product0.getCostingLevel(as);
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel0)) {
@ -367,13 +381,15 @@ public class MID_DocProduction extends Doc
// get cost of children
MCostDetail cd0 = MCostDetail.get(as.getCtx(), "M_ProductionLine_ID=?",
line0.get_ID(), ma.getM_AttributeSetInstance_ID(), as.getC_AcctSchema_ID(), getTrxName());
if (cd0 != null)
if (cd0 != null) {
maCost = cd0.getAmt();
else {
log.info("[8.1] CostDetail: " + cd0.get_ID() + " - " + maCost);
} else {
ProductCost pc = line0.getProductCost();
pc.setQty(ma.getMovementQty());
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
maCost = line0.getProductCosts(as, line0.getAD_Org_ID(), false);
log.info("[8.2] ProductCost: " + line0.get_ID() + " - " + maCost);
}
costMap.put(line0.get_ID() + "_" + ma.getM_AttributeSetInstance_ID(), maCost);
costs0 = costs0.add(maCost);
@ -389,8 +405,10 @@ public class MID_DocProduction extends Doc
BigDecimal costs0;
if (cd0 != null) {
costs0 = cd0.getAmt();
log.info("[8.3] CostDetail: " + cd0.get_ID() + " - " + costs0);
} else {
costs0 = line0.getProductCosts(as, line0.getAD_Org_ID(), false);
log.info("[8.4] ProductCost: " + line0.get_ID() + " - " + costs0);
}
costMap.put(line0.get_ID() + "_" + line0.getM_AttributeSetInstance_ID(), costs0);
bomCost = bomCost.add(costs0);
@ -403,34 +421,37 @@ public class MID_DocProduction extends Doc
BigDecimal costs0;
if (cd0 != null) {
costs0 = cd0.getAmt();
log.info("[8.5] CostDetail: " + cd0.get_ID() + " - " + costs0);
} else {
costs0 = line0.getProductCosts(as, line0.getAD_Org_ID(), false);
log.info("[8.6] ProductCost: " + line0.get_ID() + " - " + costs0);
}
costMap.put(line0.get_ID() + "_" + line0.getM_AttributeSetInstance_ID(), costs0);
bomCost = bomCost.add(costs0);
}
}
}
log.info("BOM Cost: " + bomCost + ", Total Charge Amount: " + totalChargeAmt);
log.info("[9] BOM Cost: " + bomCost + ", Total Charge Amount: " + totalChargeAmt);
qtyProduced = manipulateQtyProduced(mQtyProduced, endProLine, prod.isUseProductionPlan(), null);
if (line.getQty().compareTo(qtyProduced) != 0) {
BigDecimal factor = line.getQty().divide(qtyProduced, 12, RoundingMode.HALF_UP);
bomCost = bomCost.multiply(factor);
}
// if bomCost is negative, it means charge cost, so we need to negate it
if (bomCost.signum() < 0) {
bomCost = bomCost.add(totalChargeAmt.negate());
}else {
bomCost = bomCost.add(totalChargeAmt);
}
log.info("Final BOM Cost: " + bomCost + ", Total Charge Amount: " + totalChargeAmt);
bomCost = bomCost.add(totalChargeAmt.negate());
// if (bomCost.signum() < 0) {
// bomCost = bomCost.add(totalChargeAmt.negate());
// }else {
// bomCost = bomCost.add(totalChargeAmt);
// }
log.info("[10] Final BOM Cost: " + bomCost + ", Total Charge Amount: " + totalChargeAmt);
// add total charge amount to BOM cost
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel)) {
//post roll-up
log.info("COSTINGLEVEL_BatchLot "+bomCost.setScale(stdPrecision, RoundingMode.HALF_UP));
log.info("[11] COSTINGLEVEL_BatchLot "+bomCost.setScale(stdPrecision, RoundingMode.HALF_UP));
fl = fact.createLine(line,
line.getAccount(ProductCost.ACCTTYPE_P_Asset, as),
as.getC_Currency_ID(), bomCost.setScale(stdPrecision, RoundingMode.HALF_UP));
@ -442,9 +463,11 @@ public class MID_DocProduction extends Doc
} else if (MAcctSchema.COSTINGMETHOD_StandardCosting.equals(costingMethod)) {
BigDecimal variance = costs.subtract(bomCost).setScale(stdPrecision, RoundingMode.HALF_UP);
// only post variance if it's not zero
log.info("COSTINGMETHOD_StandardCosting "+variance);
log.info("[12] COSTINGMETHOD_StandardCosting "+variance);
if (variance.signum() != 0) {
//post variance
log.info("[13] Posting Variance for Standard Costing: " + variance.setScale(stdPrecision, RoundingMode.HALF_UP)
+ ", Product=" + product.getName() + " - " + product.getValue());
fl = fact.createLine(line,
line.getAccount(ProductCost.ACCTTYPE_P_RateVariance, as),
as.getC_Currency_ID(), variance.negate());
@ -462,9 +485,21 @@ public class MID_DocProduction extends Doc
// Inventory DR CR
if (!(line.isProductionBOM() && MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel))) {
BigDecimal factLineAmt = costs;
log.info("[14] Fact Line Amount before BOM check: " + factLineAmt.setScale(stdPrecision, RoundingMode.HALF_UP)
+ ", Product=" + product.getName() + " - " + product.getValue());
if (line.isProductionBOM() && !(MAcctSchema.COSTINGMETHOD_StandardCosting.equals(costingMethod))) {
log.info("[15.1] isProductionBOM and not Standard Costing, negate bomCost: " + bomCost.setScale(stdPrecision, RoundingMode.HALF_UP)
+ ", Product=" + product.getName() + " - " + product.getValue());
factLineAmt = bomCost.negate();
}
log.info("[15.2] Creating Fact Line for Inventory: " + factLineAmt.setScale(stdPrecision, RoundingMode.HALF_UP)
+ ", Product=" + product.getName() + " - " + product.getValue());
// is end product
if(prodline.isEndProduct() && isEndProductWithAnyLinesChargeAndNoMaterial) {
// if end product with any lines charge and no material, then inventory account from charge
log.info("[15.3] isEndProductWithAnyLinesChargeAndNoMaterial, get inventory account from charge");
factLineAmt = factLineAmt.negate();
}
fl = fact.createLine(line,
line.getAccount(ProductCost.ACCTTYPE_P_Asset, as),
as.getC_Currency_ID(), factLineAmt.setScale(stdPrecision, RoundingMode.HALF_UP));
@ -484,6 +519,8 @@ public class MID_DocProduction extends Doc
description += "(*)";
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel)) {
if (line.isProductionBOM()) {
log.info("[16] Posting Cost Detail for BOM Line: " + line.getLine() + " - " + line.getDescription()
+ ", Product=" + product.getName() + " - " + product.getValue());
if (!MCostDetail.createProduction(as, line.getAD_Org_ID(),
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
line.get_ID(), 0,
@ -500,6 +537,10 @@ public class MID_DocProduction extends Doc
{
MProductionLineMA ma = mas[j];
BigDecimal maCost = costMap.get(line.get_ID() + "_" + ma.getM_AttributeSetInstance_ID());
log.info("[17] Posting Cost Detail for MA Line: " + line.getLine() + " - " + line.getDescription()
+ ", Product=" + product.getName() + " - " + product.getValue()
+ ", ASI=" + ma.getM_AttributeSetInstance_ID() + ", Qty=" + ma.getMovementQty()
+ ", Cost=" + maCost);
if (!MCostDetail.createProduction(as, line.getAD_Org_ID(),
line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),
line.get_ID(), 0,
@ -513,6 +554,10 @@ public class MID_DocProduction extends Doc
}
else
{
log.info("[18] Posting Cost Detail for Line: " + line.getLine() + " - " + line.getDescription()
+ ", Product=" + product.getName() + " - " + product.getValue()
+ ", ASI=" + line.getM_AttributeSetInstance_ID() + ", Qty=" + line.getQty()
+ ", Cost=" + costs);
if (!MCostDetail.createProduction(as, line.getAD_Org_ID(),
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
line.get_ID(), 0,
@ -525,7 +570,7 @@ public class MID_DocProduction extends Doc
}
} else {
if (line.isProductionBOM() && !(MAcctSchema.COSTINGMETHOD_StandardCosting.equals(costingMethod))) {
log.info("Posting Cost Detail for BOM Line: " + line.getLine() + " - " + line.getDescription()
log.info("[19] Posting Cost Detail for BOM Line: " + line.getLine() + " - " + line.getDescription()
+ ", Product=" + product.getName() + " - " + product.getValue());
if (!MCostDetail.createProduction(as, line.getAD_Org_ID(),
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
@ -537,6 +582,10 @@ public class MID_DocProduction extends Doc
return null;
}
} else {
log.info("[20] Posting Cost Detail for Line: " + line.getLine() + " - " + line.getDescription()
+ ", Product=" + product.getName() + " - " + product.getValue()
+ ", ASI=" + line.getM_AttributeSetInstance_ID() + ", Qty=" + line.getQty()
+ ", Cost=" + costs);
if (!MCostDetail.createProduction(as, line.getAD_Org_ID(),
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
line.get_ID(), 0,

View File

@ -1,6 +1,7 @@
package balinusa.midsuit.model;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.List;
@ -18,6 +19,7 @@ import org.compiere.model.MDocType;
import org.compiere.model.MInventory;
import org.compiere.model.MInventoryLine;
import org.compiere.model.MInventoryLineMA;
import org.compiere.model.MMovementLine;
import org.compiere.model.MPeriod;
import org.compiere.model.MProduct;
import org.compiere.model.MStorageOnHand;
@ -519,6 +521,63 @@ public class MID_MInventory extends MInventory {
}
return super.afterSave(newRecord, success);
}else if (get_ValueAsInt("M_Movement_ID") != 0 ) {
int M_Movement_ID = get_ValueAsInt("M_Movement_ID");
// Perform actions related to M_Movement_ID
MID_MMovement movement = new MID_MMovement(getCtx(), M_Movement_ID, get_TrxName());
MMovementLine[] lines = movement.getLines(true);
int chargeID = DB.getSQLValueEx(null, " SELECT cc.C_Charge_ID FROM C_ChargeType_DocType cctd JOIN C_Charge cc ON cc.C_ChargeType_ID = cctd.C_ChargeType_ID WHERE cctd.C_DocType_ID = ? LIMIT 1 ", getC_DocType_ID());
// filter and sum MovementQty by M_Product_ID and M_LocatorTo_ID
StringBuffer sql = new StringBuffer("SELECT MIN(sl.M_MovementLine_ID) AS M_MovementLine_ID, ")
.append(" sl.M_Product_ID, ")
.append(" sl.M_LocatorTo_ID, ")
.append(" SUM(sl.MovementQty) AS MovementQty ")
.append("FROM M_MovementLine sl ")
.append("WHERE sl.M_Movement_ID=? ") // filter header jika perlu
.append("GROUP BY sl.M_Product_ID, sl.M_LocatorTo_ID");
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = DB.prepareStatement(sql.toString(), get_TrxName());
pstmt.setInt(1, M_Movement_ID); // set parameter header jika perlu
rs = pstmt.executeQuery();
// create array list to store filtered lines
while (rs.next()) {
MMovementLine line = new MMovementLine(getCtx(), rs.getInt("M_MovementLine_ID"), get_TrxName());
MInventoryLine inventoryLine = new MInventoryLine(getCtx(), 0, get_TrxName());
inventoryLine.setAD_Org_ID(getAD_Org_ID());
inventoryLine.setM_Inventory_ID(getM_Inventory_ID());
inventoryLine.setC_Charge_ID(chargeID);
inventoryLine.setM_Product_ID(line.getM_Product_ID());
inventoryLine.setM_Locator_ID(line.getM_LocatorTo_ID());
inventoryLine.set_ValueOfColumn("QtyEntered", rs.getBigDecimal("MovementQty"));;
inventoryLine.setQtyInternalUse(rs.getBigDecimal("MovementQty"));
inventoryLine.saveEx();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
// MMovementLine[] filteredLines = sumMovementLines(lines);
//
// for (MMovementLine line : lines) {
// // create inventory line for each movement line
// MInventoryLine inventoryLine = new MInventoryLine(getCtx(), 0, get_TrxName());
//
// BigDecimal existingQtyInternalUse = inventoryLine.getQtyInternalUse() != null ? inventoryLine.getQtyInternalUse() : BigDecimal.ZERO;
// BigDecimal newQtyInternalUse = existingQtyInternalUse.add(line.getMovementQty());
// inventoryLine.setAD_Org_ID(getAD_Org_ID());
// inventoryLine.setM_Inventory_ID(getM_Inventory_ID());
// inventoryLine.setC_Charge_ID(chargeID);
// inventoryLine.setM_Product_ID(line.getM_Product_ID());
// inventoryLine.setM_Locator_ID(line.getM_LocatorTo_ID());
// inventoryLine.set_ValueOfColumn("QtyEntered", newQtyInternalUse);;
// inventoryLine.setQtyInternalUse(newQtyInternalUse);
// inventoryLine.saveEx();
// }
}
return super.afterSave(newRecord, success);
@ -527,5 +586,34 @@ public class MID_MInventory extends MInventory {
return super.afterSave(newRecord, success);
}
private MMovementLine[] sumMovementLines(MMovementLine[] lines) {
// TODO Auto-generated method stub
// create new array list
MMovementLine[] summedLines = new MMovementLine[lines.length];
// loop through lines
for (MMovementLine line : lines) {
boolean found = false;
for (int i = 0; i < summedLines.length; i++) {
if (summedLines[i] != null && summedLines[i].getM_Product_ID() == line.getM_Product_ID()
&& summedLines[i].getM_LocatorTo_ID() == line.getM_LocatorTo_ID()) {
// if product and locator already in array list, sum the movement qty
summedLines[i].setMovementQty(summedLines[i].getMovementQty().add(line.getMovementQty()));
found = true;
break;
}
}
if (!found) {
// if product and locator not in array list, add to array list
for (int i = 0; i < summedLines.length; i++) {
if (summedLines[i] == null) {
summedLines[i] = line;
break;
}
}
}
}
// check if product and locator already in array list
return summedLines;
}
}

View File

@ -12,6 +12,7 @@ import org.compiere.model.I_M_InventoryLineMA;
import org.compiere.model.MInventoryLine;
import org.compiere.model.MInventoryLineMA;
import org.compiere.model.MStorageOnHand;
import org.compiere.model.PO;
import org.compiere.model.Query;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
@ -46,17 +47,53 @@ public class MID_MInventoryLineMA extends MInventoryLineMA {
return addOrCreate(line, M_AttributeSetInstance_ID, MovementQty,DateMaterialPolicy,true);
}
public static MID_MInventoryLineMA addOrCreate(MInventoryLine line, int M_AttributeSetInstance_ID, BigDecimal MovementQty, Timestamp DateMaterialPolicy,boolean isAutoGenerated)
// Language: Java
public static MID_MInventoryLineMA addOrCreate(
MInventoryLine line,
int M_AttributeSetInstance_ID,
BigDecimal MovementQty,
Timestamp DateMaterialPolicy,
boolean isAutoGenerated)
{
Query query = new Query(Env.getCtx(), I_M_InventoryLineMA.Table_Name, "M_InventoryLine_ID=? AND M_AttributeSetInstance_ID=? AND DateMaterialPolicy=trunc(cast(? as date))",
line.get_TrxName());
MID_MInventoryLineMA po = query.setParameters(line.getM_InventoryLine_ID(), M_AttributeSetInstance_ID, DateMaterialPolicy).first();
if (po == null)
po = new MID_MInventoryLineMA(line, M_AttributeSetInstance_ID, MovementQty, DateMaterialPolicy,isAutoGenerated);
else
po.setMovementQty(po.getMovementQty().add(MovementQty));
return po;
// Guard: pastikan qty ga null
if (MovementQty == null)
MovementQty = Env.ZERO;
// Cari existing row (kombinasi unik: Line + ASI + DateMaterialPolicy)
Query query = new Query(
Env.getCtx(),
I_M_InventoryLineMA.Table_Name,
"M_InventoryLine_ID=? AND M_AttributeSetInstance_ID=? AND DateMaterialPolicy=trunc(cast(? as date))",
line.get_TrxName());
PO base = query
.setParameters(line.getM_InventoryLine_ID(), M_AttributeSetInstance_ID, DateMaterialPolicy)
.first();
MID_MInventoryLineMA po;
if (base != null) {
// Muat ulang row yang sama tapi pakai subclass kita
po = new MID_MInventoryLineMA(Env.getCtx(), base.get_ID(), line.get_TrxName());
BigDecimal current = po.getMovementQty();
if (current == null) current = Env.ZERO;
po.setMovementQty(current.add(MovementQty));
} else {
// Buat baru pakai constructor subclass
po = new MID_MInventoryLineMA(line, M_AttributeSetInstance_ID, MovementQty, DateMaterialPolicy, isAutoGenerated);
}
// Tergantung pola proyekmu: simpan di sini agar atomic,
// atau kembalikan object lalu disave di layer pemanggil.
po.saveEx();
return po;
}
public static MID_MInventoryLineMA[] get (Properties ctx, int M_InventoryLine_ID, String trxName)
{

View File

@ -38,10 +38,12 @@ import org.compiere.model.MPayment;
import org.compiere.model.MPaymentProcessor;
import org.compiere.model.MPaymentTransaction;
import org.compiere.model.MPeriod;
import org.compiere.model.MProduct;
import org.compiere.model.MProductionLine;
import org.compiere.model.MProject;
import org.compiere.model.MRMALine;
import org.compiere.model.MSysConfig;
import org.compiere.model.MUOMConversion;
import org.compiere.model.MUser;
import org.compiere.model.MWarehouse;
import org.compiere.model.MatchPOAutoMatch;
@ -64,6 +66,7 @@ public class MID_MInvoice extends MInvoice implements DocAction{
*
*/
private static final long serialVersionUID = 5957690840999925125L;
private static CLogger s_log = CLogger.getCLogger(MInvoice.class);
public MID_MInvoice(Properties ctx, int C_Order_ID, String trxName) {
super(ctx, C_Order_ID, trxName);
@ -171,7 +174,142 @@ public class MID_MInvoice extends MInvoice implements DocAction{
}
return bankAccount;
}
/**
* Create Line from orderline/inoutline/rmaline
* @param C_OrderLine_ID
* @param M_InOutLine_ID
* @param M_RMALine_ID
* @param M_Product_ID
* @param C_UOM_ID
* @param Qty
*/
public void createLineFrom(int C_OrderLine_ID, int M_InOutLine_ID, int M_RMALine_ID,
int M_Product_ID, int C_UOM_ID, BigDecimal Qty)
{
MInvoiceLine invoiceLine = new MInvoiceLine (this);
invoiceLine.setM_Product_ID(M_Product_ID, C_UOM_ID); // Line UOM
invoiceLine.setQty(Qty); // Invoiced/Entered
BigDecimal QtyInvoiced = null;
MProduct product = MProduct.get(Env.getCtx(), M_Product_ID);
if (M_Product_ID > 0 && product.getC_UOM_ID() != C_UOM_ID) {
QtyInvoiced = MUOMConversion.convertProductFrom(Env.getCtx(), M_Product_ID, C_UOM_ID, Qty);
}
if (QtyInvoiced == null)
QtyInvoiced = Qty;
invoiceLine.setQtyInvoiced(QtyInvoiced);
// Info
MOrderLine orderLine = null;
if (C_OrderLine_ID != 0)
orderLine = new MOrderLine (Env.getCtx(), C_OrderLine_ID, get_TrxName());
//
MRMALine rmaLine = null;
if (M_RMALine_ID > 0)
rmaLine = new MRMALine (Env.getCtx(), M_RMALine_ID, get_TrxName());
//
MInOutLine inoutLine = null;
if (M_InOutLine_ID != 0)
{
inoutLine = new MInOutLine (Env.getCtx(), M_InOutLine_ID, get_TrxName());
if (orderLine == null && inoutLine.getC_OrderLine_ID() != 0)
{
C_OrderLine_ID = inoutLine.getC_OrderLine_ID();
orderLine = new MOrderLine (Env.getCtx(), C_OrderLine_ID, get_TrxName());
}
}
else if (C_OrderLine_ID > 0)
{
String whereClause = "EXISTS (SELECT 1 FROM M_InOut io WHERE io.M_InOut_ID=M_InOutLine.M_InOut_ID AND io.DocStatus IN ('CO','CL'))";
MInOutLine[] lines = MInOutLine.getOfOrderLine(Env.getCtx(),
C_OrderLine_ID, whereClause, get_TrxName());
if (s_log.isLoggable(Level.FINE)) s_log.fine ("Receipt Lines with OrderLine = #" + lines.length);
if (lines.length > 0)
{
for (int j = 0; j < lines.length; j++)
{
MInOutLine line = lines[j];
// qty matched
BigDecimal qtyMatched = Env.ZERO;
for (MMatchInv match : MMatchInv.getInOutLine(Env.getCtx(), line.getM_InOutLine_ID(), get_TrxName())) {
qtyMatched = qtyMatched.add(match.getQty());
}
if (line.getQtyEntered().subtract(qtyMatched).compareTo(Qty) == 0)
{
inoutLine = line;
M_InOutLine_ID = inoutLine.getM_InOutLine_ID();
break;
}
}
}
}
else if (M_RMALine_ID != 0)
{
String whereClause = "EXISTS (SELECT 1 FROM M_InOut io WHERE io.M_InOut_ID=M_InOutLine.M_InOut_ID AND io.DocStatus IN ('CO','CL'))";
MInOutLine[] lines = MInOutLine.getOfRMALine(Env.getCtx(), M_RMALine_ID, whereClause, get_TrxName());
if (s_log.isLoggable(Level.FINE)) s_log.fine ("Receipt Lines with RMALine = #" + lines.length);
if (lines.length > 0)
{
for (int j = 0; j < lines.length; j++)
{
MInOutLine line = lines[j];
BigDecimal alreadyInvoiced = rmaLine.getQtyInvoiced() != null ? rmaLine.getQtyInvoiced() : BigDecimal.ZERO;
if (rmaLine.getQty().subtract(alreadyInvoiced).compareTo(Qty) >= 0)
{
inoutLine = line;
M_InOutLine_ID = inoutLine.getM_InOutLine_ID();
break;
}
}
if (rmaLine == null)
{
inoutLine = lines[0]; // first as default
M_InOutLine_ID = inoutLine.getM_InOutLine_ID();
}
}
}
// get Ship info
// Shipment Info
if (inoutLine != null)
{
invoiceLine.setShipLine(inoutLine); // overwrites
if(invoiceLine.getC_UOM_ID()!=inoutLine.getC_UOM_ID()) {
invoiceLine.setC_UOM_ID(inoutLine.getC_UOM_ID());
BigDecimal PriceEntered = MUOMConversion.convertProductFrom (Env.getCtx(), M_Product_ID,
inoutLine.getC_UOM_ID(), invoiceLine.getPriceEntered(), 12);
if (PriceEntered == null)
throw new AdempiereException("No Conversion For Price=" + invoiceLine.getPriceEntered());
invoiceLine.setPriceEntered(PriceEntered);
}
}
else {
if (s_log.isLoggable(Level.FINE)) s_log.fine("No Receipt Line");
// Order Info
if (orderLine != null)
{
invoiceLine.setOrderLine(orderLine); // overwrites
}
else
{
if (s_log.isLoggable(Level.FINE)) s_log.fine("No Order Line");
invoiceLine.setPrice();
invoiceLine.setTax();
}
//RMA Info
if (rmaLine != null)
{
invoiceLine.setRMALine(rmaLine); // overwrites
}
else
{
if (s_log.isLoggable(Level.FINE)) s_log.fine("No RMA Line");
}
}
invoiceLine.saveEx();
}
/**
* Complete Document
* @return new status (Complete, In Progress, Invalid, Waiting ..)

View File

@ -549,6 +549,10 @@ public class MID_MPayment extends MPayment{
}
}
}
if(newRecord && getC_Order_ID() > 0 && !isPrepayment()) {
setIsPrepayment(true);
}
return true;
} // beforeSave

View File

@ -20,6 +20,7 @@ import org.compiere.model.MProduct;
import org.compiere.model.MProductCategory;
import org.compiere.model.MProduction;
import org.compiere.model.MProductionLine;
import org.compiere.model.MProductionLineMA;
import org.compiere.model.MProductionPlan;
import org.compiere.model.MRequisitionLine;
import org.compiere.model.MSequence;
@ -32,6 +33,7 @@ import org.compiere.model.MTreeNode;
import org.compiere.model.MTree_Base;
import org.compiere.model.ModelValidationEngine;
import org.compiere.model.ModelValidator;
import org.compiere.model.PO;
import org.compiere.model.Query;
import org.compiere.model.X_AD_Tree;
import org.compiere.model.X_M_CostElement;
@ -50,6 +52,9 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
@ -82,6 +87,23 @@ public class MID_Production extends MProduction{
return false;
}
/**
* @param lines
* @return true if one of the production is an end product line (IsEndProduct=Y) and any materials minimum 1 and quantity.
*/
private boolean isHaveEndProductAndMaterials(MProductionLine[] lines) {
boolean isHaveEndProduct = false;
boolean isHaveMaterials = false;
for(MProductionLine line : lines) {
if(line.isEndProduct())
isHaveEndProduct = true;
else if(line.getM_Product_ID() > 0 && line.getQtyUsed().compareTo(Env.ZERO) > 0)
isHaveMaterials = true;
}
return isHaveEndProduct && isHaveMaterials;
}
/**
* Create production line
* @param mustBeStocked true to verify BOM component has sufficient on hand
@ -115,7 +137,238 @@ public class MID_Production extends MProduction{
return count;
}
@Override
public boolean reverseCorrectIt()
{
if (log.isLoggable(Level.INFO)) log.info(toString());
// Before reverseCorrect
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT);
if (m_processMsg != null)
return false;
MProduction reversal = reverse(false);
if (reversal == null)
return false;
// After reverseCorrect
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT);
if (m_processMsg != null)
return false;
m_processMsg = reversal.getDocumentNo();
return true;
}
/**
* Create new production from this production (including lines)
* @param reversalDate movement date
* @return new production record
*/
protected MID_Production copyFrom(Timestamp reversalDate) {
MID_Production to = new MID_Production(getCtx(), 0, get_TrxName());
PO.copyValues (this, to, getAD_Client_ID(), getAD_Org_ID());
to.set_ValueNoCheck ("DocumentNo", null);
//
to.setDocStatus (DOCSTATUS_Drafted); // Draft
to.setDocAction(DOCACTION_Complete);
to.setMovementDate(reversalDate);
to.setIsComplete(false);
to.setIsCreated("Y");
to.setProcessing(false);
to.setProcessed(false);
to.setIsUseProductionPlan(isUseProductionPlan());
if (isUseProductionPlan()) {
to.saveEx();
Query planQuery = new Query(Env.getCtx(), I_M_ProductionPlan.Table_Name, "M_ProductionPlan.M_Production_ID=?", get_TrxName());
List<MProductionPlan> fplans = planQuery.setParameters(getM_Production_ID()).list();
for(MProductionPlan fplan : fplans) {
MProductionPlan tplan = new MProductionPlan(getCtx(), 0, get_TrxName());
PO.copyValues (fplan, tplan, getAD_Client_ID(), getAD_Org_ID());
tplan.setM_Production_ID(to.getM_Production_ID());
tplan.setProductionQty(fplan.getProductionQty().negate());
tplan.setProcessed(false);
tplan.saveEx();
MProductionLine[] flines = fplan.getLines();
for(MProductionLine fline : flines) {
MProductionLine tline = new MProductionLine(tplan);
PO.copyValues (fline, tline, getAD_Client_ID(), getAD_Org_ID());
tline.setM_ProductionPlan_ID(tplan.getM_ProductionPlan_ID());
tline.setMovementQty(fline.getMovementQty().negate());
tline.setPlannedQty(fline.getPlannedQty().negate());
tline.setQtyUsed(fline.getQtyUsed().negate());
tline.saveEx();
}
}
} else {
to.setProductionQty(getProductionQty().negate());
to.saveEx();
MProductionLine[] flines = getLinesWithCharge();
log.info("Lines to reverse: " + flines.length);
for(MProductionLine fline : flines) {
MProductionLine tline = new MProductionLine(to);
PO.copyValues (fline, tline, getAD_Client_ID(), getAD_Org_ID());
tline.setM_Production_ID(to.getM_Production_ID());
tline.setMovementQty(fline.getMovementQty().negate());
tline.setPlannedQty(fline.getPlannedQty().negate());
tline.setQtyUsed(fline.getQtyUsed().negate());
if(fline.get_Value("C_Charge_ID") != null) {
BigDecimal priceEntered = (BigDecimal) fline.get_Value("PriceEntered");
BigDecimal quantity = fline.getQtyUsed().negate();
tline.set_ValueOfColumn("C_Charge_ID", fline.get_Value("C_Charge_ID"));
tline.setM_Product_ID(0);
tline.set_ValueOfColumn("PriceEntered", fline.get_Value("PriceEntered"));
tline.set_ValueOfColumn("LineNetAmt", priceEntered.multiply(quantity));
};
tline.saveEx();
}
}
return to;
}
/**
* Reverse this production document
* @param accrual true to use current date, false to use this production's movement date
* @return reversal production record
*/
protected MProduction reverse(boolean accrual) {
Timestamp reversalDate = accrual ? Env.getContextAsDate(getCtx(), Env.DATE) : getMovementDate();
if (reversalDate == null) {
reversalDate = new Timestamp(System.currentTimeMillis());
}
if (getC_OrderLine_ID() > 0)
setC_OrderLine_ID(0);
// if (isProcessed()) {
// setProcessed(false);
// saveEx(get_TrxName());
// }
MPeriod.testPeriodOpen(getCtx(), reversalDate, getC_DocType_ID(), getAD_Org_ID());
MID_Production reversal = null;
reversal = copyFrom(reversalDate);
StringBuilder msgadd = new StringBuilder("{->").append(getDocumentNo()).append(")");
reversal.addDescription(msgadd.toString());
reversal.setReversal_ID(getM_Production_ID());
reversal.saveEx(get_TrxName());
// Reverse Line Qty
MProductionLine[] sLines = getLinesWithCharge();
MProductionLine[] tLines = reversal.getLinesWithCharge();
for (int i = 0; i < sLines.length; i++)
{
// We need to copy MA and required product
if (sLines[i].getM_AttributeSetInstance_ID() == 0 && sLines[i].getM_Product_ID() != 0)
{
MProductionLineMA mas[] = MProductionLineMA.get(getCtx(), sLines[i].get_ID(), get_TrxName());
for (int j = 0; j < mas.length; j++)
{
MProductionLineMA ma = new MProductionLineMA (tLines[i],
mas[j].getM_AttributeSetInstance_ID(),
mas[j].getMovementQty().negate(),mas[j].getDateMaterialPolicy());
ma.saveEx(get_TrxName());
}
}
}
if (!reversal.processIt(DocAction.ACTION_Complete))
{
m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg();
return null;
}
reversal.closeIt();
reversal.setProcessing (false);
reversal.setDocStatus(DOCSTATUS_Reversed);
reversal.setDocAction(DOCACTION_None);
reversal.saveEx(get_TrxName());
msgadd = new StringBuilder("(").append(reversal.getDocumentNo()).append("<-)");
addDescription(msgadd.toString());
setProcessed(true);
setReversal_ID(reversal.getM_Production_ID());
setDocStatus(DOCSTATUS_Reversed); // may come from void
setDocAction(DOCACTION_None);
return reversal;
}
/**
* Get production lines
* @return array of MProductionLine
*/
public MProductionLine[] getLines() {
ArrayList<MProductionLine> list = new ArrayList<MProductionLine>();
String sql = "SELECT pl.M_ProductionLine_ID "
+ "FROM M_ProductionLine pl "
+ "WHERE pl.m_product_id is not null and pl.M_Production_ID = ? "
+ "ORDER BY pl.Line, pl.M_ProductionLine_ID ";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql, get_TrxName());
pstmt.setInt(1, get_ID());
rs = pstmt.executeQuery();
while (rs.next())
list.add( new MProductionLine( getCtx(), rs.getInt(1), get_TrxName() ) );
}
catch (SQLException ex)
{
throw new AdempiereException("Unable to load production lines", ex);
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
MProductionLine[] retValue = new MProductionLine[list.size()];
list.toArray(retValue);
return retValue;
}
/**
* Get production lines
* @return array of MProductionLine
*/
public MProductionLine[] getLinesWithCharge() {
ArrayList<MProductionLine> list = new ArrayList<MProductionLine>();
String sql = "SELECT pl.M_ProductionLine_ID "
+ "FROM M_ProductionLine pl "
+ "WHERE pl.M_Production_ID = ? "
+ "ORDER BY pl.Line, pl.M_ProductionLine_ID ";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql, get_TrxName());
pstmt.setInt(1, get_ID());
rs = pstmt.executeQuery();
while (rs.next())
list.add( new MProductionLine( getCtx(), rs.getInt(1), get_TrxName() ) );
}
catch (SQLException ex)
{
throw new AdempiereException("Unable to load production lines", ex);
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
MProductionLine[] retValue = new MProductionLine[list.size()];
list.toArray(retValue);
return retValue;
}
/**
* Create production line
* @param mustBeStocked true to verify BOM component has sufficient on hand
@ -406,8 +659,14 @@ public class MID_Production extends MProduction{
if ( getIsCreated().equals("N") )
{
m_processMsg = "Not created";
return DocAction.STATUS_Invalid;
// get lines length
MProductionLine[] lines = getLines();
if(lines.length == 0) {
m_processMsg = "No production lines";
return DocAction.STATUS_Invalid;
}
// m_processMsg = "Not created";
// return DocAction.STATUS_Invalid;
}
// !isUseProductionPlan()
log.info("END PRODUCT: " + !isUseProductionPlan());
@ -469,6 +728,10 @@ public class MID_Production extends MProduction{
m_processMsg = "Production does not contain End Product";
return DocAction.STATUS_Invalid;
}
// if(!isHaveEndProductAndMaterials(lines)) {
// m_processMsg = "Production must contain Materials";
// return DocAction.STATUS_Invalid;
// }
errors.append(processLines(lines));
if (errors.length() > 0) {
m_processMsg = errors.toString();
@ -477,8 +740,8 @@ public class MID_Production extends MProduction{
processed = processed + lines.length;
} else {
Query planQuery = new Query(Env.getCtx(), I_M_ProductionPlan.Table_Name, "M_ProductionPlan.M_Production_ID=?", get_TrxName());
List<MProductionPlan> plans = planQuery.setParameters(getM_Production_ID()).list();
for(MProductionPlan plan : plans) {
List<MID_ProductionPlan> plans = planQuery.setParameters(getM_Production_ID()).list();
for(MID_ProductionPlan plan : plans) {
MProductionLine[] lines = plan.getLines();
//IDEMPIERE-3107 Check if End Product in Production Lines exist
@ -486,6 +749,11 @@ public class MID_Production extends MProduction{
m_processMsg = String.format("Production plan (line %1$d id %2$d) does not contain End Product", plan.getLine(), plan.get_ID());
return DocAction.STATUS_Invalid;
}
// if(!isHaveEndProductAndMaterials(lines)) {
// m_processMsg = "Production must contain Materials";
// return DocAction.STATUS_Invalid;
// }
if (lines.length > 0) {
errors.append(processLines(lines));

View File

@ -0,0 +1,122 @@
package balinusa.midsuit.model;
import java.util.Properties;
import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.I_M_ProductionPlan;
import org.compiere.model.MAccount;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MAttributeSetInstance;
import org.compiere.model.MClient;
import org.compiere.model.MClientInfo;
import org.compiere.model.MCost;
import org.compiere.model.MCostElement;
import org.compiere.model.MLocator;
import org.compiere.model.MOrderLine;
import org.compiere.model.MOrg;
import org.compiere.model.MPeriod;
import org.compiere.model.MProduct;
import org.compiere.model.MProductCategory;
import org.compiere.model.MProduction;
import org.compiere.model.MProductionLine;
import org.compiere.model.MProductionLineMA;
import org.compiere.model.MProductionPlan;
import org.compiere.model.MRequisitionLine;
import org.compiere.model.MSequence;
import org.compiere.model.MStorageOnHand;
import org.compiere.model.MStorageReservation;
import org.compiere.model.MSysConfig;
import org.compiere.model.MTable;
import org.compiere.model.MTree;
import org.compiere.model.MTreeNode;
import org.compiere.model.MTree_Base;
import org.compiere.model.ModelValidationEngine;
import org.compiere.model.ModelValidator;
import org.compiere.model.Query;
import org.compiere.model.X_AD_Tree;
import org.compiere.model.X_M_CostElement;
import org.compiere.model.X_M_Product;
import org.compiere.process.DocAction;
import org.compiere.util.AdempiereUserError;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.eevolution.model.MPPProductBOM;
import org.eevolution.model.MPPProductBOMLine;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
public class MID_ProductionPlan extends MProductionPlan{
/**
*
*/
private static final long serialVersionUID = 1563913040977233621L;
private boolean isValueEmptyBeforeSave = false;
private static CLogger s_log = CLogger.getCLogger (MCost.class);
protected int lineno;
protected int count;
public MID_ProductionPlan(Properties ctx, String M_ProductionPlan_UU, String trxName) {
super(ctx, M_ProductionPlan_UU, trxName);
}
/**
* @param ctx
* @param M_ProductionPlan_ID
* @param trxName
*/
public MID_ProductionPlan(Properties ctx, int M_ProductionPlan_ID, String trxName) {
super(ctx, M_ProductionPlan_ID, trxName);
}
/**
* @return array of MProductionLine
*/
public MProductionLine[] getLines() {
ArrayList<MProductionLine> list = new ArrayList<MProductionLine>();
String sql = "SELECT pl.M_ProductionLine_ID "
+ "FROM M_ProductionLine pl "
+ "WHERE pl.m_product_id is not null and pl.M_ProductionPlan_ID = ? "
+ "ORDER BY pl.Line, pl.M_ProductionLine_ID ";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql, get_TrxName());
pstmt.setInt(1, get_ID());
rs = pstmt.executeQuery();
while (rs.next())
list.add( new MProductionLine( getCtx(), rs.getInt(1), get_TrxName() ) );
}
catch (SQLException ex)
{
throw new AdempiereException("Unable to load production lines", ex);
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
MProductionLine[] retValue = new MProductionLine[list.size()];
list.toArray(retValue);
return retValue;
}
}

View File

@ -0,0 +1,255 @@
package balinusa.midsuit.process;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.I_M_ProductionPlan;
import org.compiere.model.MInOut;
import org.compiere.model.MInvoice;
import org.compiere.model.MOrder;
import org.compiere.model.MProcessPara;
import org.compiere.model.MProduct;
import org.compiere.model.MProduction;
import org.compiere.model.MProductionPlan;
import org.compiere.model.MRMA;
import org.compiere.model.MTable;
import org.compiere.model.Query;
import org.compiere.process.DocAction;
import org.compiere.process.ProcessInfoParameter;
import org.compiere.process.SvrProcess;
import org.compiere.util.AdempiereUserError;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Util;
import balinusa.midsuit.model.MID_MRequisitionTrx;
import balinusa.midsuit.model.MID_Production;
public class MID_InvoiceCreateLineFrom extends SvrProcess{
private int p_C_Invoice_ID = 0;
private ArrayList<Integer> selectionIDList = new ArrayList<Integer>();
private HashMap<String, Object> selectionValueMap = new HashMap<String, Object>();
private int m_created = 0;
@Override
protected void prepare() {
ProcessInfoParameter[] para = getParameter();
for (int i = 0; i < para.length; i++)
{
String name = para[i].getParameterName();
if (para[i].getParameter() == null)
;
else if (name.equals("C_Invoice_ID"))
p_C_Invoice_ID = para[i].getParameterAsInt();
else
MProcessPara.validateUnknownParameter(getProcessInfo().getAD_Process_ID(), para[i]);
}
}
@Override
protected String doIt() throws Exception {
if (p_C_Invoice_ID == 0)
throw new AdempiereUserError("@NotFound@ @C_Invoice_ID@");
if (getProcessInfo().getAD_InfoWindow_ID() > 0)
return createLines();
else
throw new AdempiereException("@NotSupported@");
}
private String createLines() {
// Get Invoice
MInvoice invoice = new MInvoice(getCtx(), p_C_Invoice_ID, get_TrxName());
if (log.isLoggable(Level.CONFIG)) log.config(invoice.toString());
StringBuilder sql = new StringBuilder();
sql.append("SELECT t.T_Selection_ID, CAST(t.ViewID AS Integer) AS AD_Table_ID ");
sql.append("FROM T_Selection t ");
sql.append("WHERE t.AD_PInstance_ID=? ");
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql.toString(), get_TrxName());
pstmt.setInt(1, getAD_PInstance_ID());
rs = pstmt.executeQuery();
while (rs.next())
{
int T_Selection_ID = rs.getInt("T_Selection_ID");
if (!selectionIDList.contains(T_Selection_ID))
selectionIDList.add(T_Selection_ID);
String ColumnName = "AD_Table_ID";
MTable table = MTable.get(rs.getInt(ColumnName));
String key = table.getKeyColumns()[0] + "_" + T_Selection_ID;
selectionValueMap.put(key, T_Selection_ID);
}
}
catch (Exception e)
{
throw new AdempiereException(e);
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
sql = new StringBuilder();
sql.append("SELECT T_Selection_ID, ColumnName, Value_String, Value_Number, Value_Date ");
sql.append("FROM T_Selection_InfoWindow ");
sql.append("WHERE AD_PInstance_ID=? ");
sql.append("ORDER BY T_Selection_ID, ColumnName ");
pstmt = null;
rs = null;
try
{
pstmt = DB.prepareStatement(sql.toString(), get_TrxName());
pstmt.setInt(1, getAD_PInstance_ID());
rs = pstmt.executeQuery();
while (rs.next())
{
int T_Selection_ID = rs.getInt("T_Selection_ID");
String ColumnName = rs.getString("ColumnName");
String Value_String = rs.getString("Value_String");
Object Value_Number = null;
if (ColumnName.toUpperCase().endsWith("_ID"))
Value_Number = rs.getInt("Value_Number");
else
Value_Number = rs.getBigDecimal("Value_Number");
Timestamp Value_Date = rs.getTimestamp("Value_Date");
String key = ColumnName + "_" + T_Selection_ID;
Object value = null;
if (Value_String != null)
value = Value_String;
else if (Value_Number != null)
value = Value_Number;
else if (Value_Date != null)
value = Value_Date;
selectionValueMap.put(key, value);
}
}
catch (Exception e)
{
throw new AdempiereException(e);
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
MOrder m_order = null;
MInOut m_inout = null;
MRMA m_rma = null;
for (int i = 0; i < selectionIDList.size(); i++)
{
int T_Selection_ID = selectionIDList.get(i);
String ColumnName = "C_Order_ID";
String key = ColumnName + "_" + T_Selection_ID;
Object value = selectionValueMap.get(key);
int C_Order_ID = value != null ? ((Number) value).intValue() : 0;
if (C_Order_ID > 0 && (m_order == null || m_order.getC_Order_ID() != C_Order_ID))
{
m_order = new MOrder(getCtx(), C_Order_ID, get_TrxName());
invoice.setOrder(m_order); // overwrite header values
invoice.saveEx();
}
ColumnName = "M_InOut_ID";
key = ColumnName + "_" + T_Selection_ID;
value = selectionValueMap.get(key);
int M_InOut_ID = value != null ? ((Number) value).intValue() : 0;
if (M_InOut_ID > 0 && (m_inout == null || m_inout.getM_InOut_ID() != M_InOut_ID))
{
m_inout = new MInOut(getCtx(), M_InOut_ID, get_TrxName());
invoice.setShipment(m_inout);
invoice.saveEx();
}
ColumnName = "M_RMA_ID";
key = ColumnName + "_" + T_Selection_ID;
value = selectionValueMap.get(key);
int M_RMA_ID = value != null ? ((Number) value).intValue() : 0;
if (M_RMA_ID > 0 && (m_rma == null || m_rma.getM_RMA_ID() != M_RMA_ID))
{
m_rma = new MRMA(getCtx(), M_RMA_ID, get_TrxName());
invoice.setM_RMA_ID(m_rma.getM_RMA_ID());
invoice.saveEx();
}
ColumnName = "Qty";
key = ColumnName + "_" + T_Selection_ID;
value = selectionValueMap.get(key);
BigDecimal QtyEntered = value != null ? (BigDecimal) value : null;
ColumnName = "C_UOM_ID";
key = ColumnName + "_" + T_Selection_ID;
value = selectionValueMap.get(key);
int C_UOM_ID = value != null ? ((Number) value).intValue() : 0;
ColumnName = "M_Product_ID";
key = ColumnName + "_" + T_Selection_ID;
value = selectionValueMap.get(key);
int M_Product_ID = value != null ? ((Number) value).intValue() : 0;
ColumnName = "C_OrderLine_ID";
key = ColumnName + "_" + T_Selection_ID;
value = selectionValueMap.get(key);
int C_OrderLine_ID = value != null ? ((Number) value).intValue() : 0;
ColumnName = "M_InOutLine_ID";
key = ColumnName + "_" + T_Selection_ID;
value = selectionValueMap.get(key);
int M_InOutLine_ID = value != null ? ((Number) value).intValue() : 0;
ColumnName = "M_RMALine_ID";
key = ColumnName + "_" + T_Selection_ID;
value = selectionValueMap.get(key);
int M_RMALine_ID = value != null ? ((Number) value).intValue() : 0;
// Precision of Qty UOM
int precision = 2;
if (M_Product_ID != 0)
{
MProduct product = MProduct.get(getCtx(), M_Product_ID);
precision = product.getUOMPrecision();
}
QtyEntered = QtyEntered.setScale(precision, RoundingMode.HALF_DOWN);
//
if (log.isLoggable(Level.FINE)) log.fine("Line QtyEntered=" + QtyEntered
+ ", Product=" + M_Product_ID
+ ", OrderLine=" + C_OrderLine_ID + ", InOutLine_ID=" + M_InOutLine_ID);
// Create new Invoice Line
invoice.createLineFrom(C_OrderLine_ID, M_InOutLine_ID, M_RMALine_ID, M_Product_ID, C_UOM_ID, QtyEntered);
m_created++;
} // for all rows
// Update Header
invoice.updateFrom(m_order);
StringBuilder msgreturn = new StringBuilder("@Created@ = ").append(m_created);
return msgreturn.toString();
}
}