I am working on recalculating percentage value of each rows when a field value on a row is updated. I have created two custom fields PoundsUsed and PercentOfWIP for INComponentTran DAC. When PoundsUsed is updated I would like to recalculate the PercentOfWIP for all INComponentTran records in Kit Assembly.
I have also created a custom field that is the sum of INComponentTran's PoundsUsed and calculating percentage based on that. Below is the code snippet that I have written to accomplish but it's only updating the latest record.
private decimal CalculateTotalLBSProduced()
{
decimal totalPortions = 0M;
decimal totalLBs = 0M;
foreach (INComponentTran tran in Base.Components.Select())
{
INTranExt tranExt = tran.GetExtension<INTranExt>();
if (tranExt.UsrPoundsUsed.HasValue)
{
if (tran.UOM == "PRTION")
{
totalPortions = totalPortions + tranExt.UsrPoundsUsed.Value;
}
if (tran.UOM == "LB")
{
totalLBs = totalLBs + tranExt.UsrPoundsUsed.Value;
}
}
}
return totalPortions - totalLBs;
}
private void CalculatePercentOfWIP()
{
decimal totalLBSProduced = CalculateTotalLBSProduced();
Base.Document.Cache.SetValueExt<INKitRegisterExt.usrLBSProduced>(Base.Document.Current, totalLBSProduced);
foreach (INComponentTran tran in Base.Components.Select())
{
decimal percentOfWIP = 0M;
Base.Components.Current = tran;
INTranExt tranExt = tran.GetExtension<INTranExt>();
if (tran.UOM == "PRTION")
{
decimal? poundsUsed = tran.GetExtension<INTranExt>().UsrPoundsUsed.GetValueOrDefault();
if (totalLBSProduced != 0 && poundsUsed.HasValue && poundsUsed.Value !=0)
{
percentOfWIP = (poundsUsed.Value / totalLBSProduced) * 100;
}
}
Base.Components.Cache.SetValueExt<INTranExt.usrPercentOfWIP>(tran, percentOfWIP);
}
}
protected void INComponentTran_UsrPoundsUsed_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e, PXFieldUpdated InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (INComponentTran)e.Row;
if (row == null) return;
CalculateTotalLBSProduced();
CalculatePercentOfWIP();
}
Thanks.
Related
I am using Acumatica 2020 R1. I have a custom text field on the PMQuote DAC. This field is in a custom tab on the project quotes screen. I want that field to always be editable. I put the following in my RowSelected event:
protected virtual void PMQuote_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected del)
{
del?.Invoke(cache, e);
PMQuote quote = e.Row as PMQuote;
cache.AllowUpdate = true;
PXUIFieldAttribute.SetEnabled(cache, e.Row, true);
PXUIFieldAttribute.SetEnabled<PMQuoteExt.usrMyCustomField>(cache, e.Row, true);
}
This didn't work, so I also looked in the automation steps. I didn't see any automation steps available to modify.
I then looked at Workflow. I didn't see any workflows to edit either. I tried creating a new one based on the status field. I added the user field for each status and made sure disabled was unchecked. This didn't work either.
Any ideas on how I can get that field to be enabled regardless of the document status?
Thanks for your help!
AllowUpdate is called only on the cache object, add it for the data view, example:
Base.Quote.AllowUpdate = true
Also, try to extend PXQuoteMaintExt graph extension:
public class PMQuoteMaintExtExtension : PXGraphExtension<PMQuoteMaintExt, PMQuoteMaint>
{
protected virtual void PMQuote_RowSelected(PXCache sender, PXRowSelectedEventArgs e, PXRowSelected sel)
{
sel?.Invoke(sender, e);
}
}
It manages visibility with RowSelected event:
namespace PX.Objects.PM
{
public class PMQuoteMaintExt : PXGraphExtension<PMDiscount, PMQuoteMaint>
{
protected virtual void PMQuote_RowSelected(PXCache sender, PXRowSelectedEventArgs e, PXRowSelected sel)
{
sel?.Invoke(sender, e);
var row = e.Row as PMQuote;
if (row == null) return;
VisibilityHandler(sender, row);
}
private void VisibilityHandler(PXCache sender, PMQuote row)
{
CR.Standalone.CROpportunityRevision revisionInDb = PXSelectReadonly<CR.Standalone.CROpportunityRevision,
Where<CR.Standalone.CROpportunityRevision.noteID, Equal<Required<CR.Standalone.CROpportunityRevision.noteID>>>>.Select(Base, row.QuoteID).FirstOrDefault();
CR.Standalone.CROpportunity opportunityInDb = (revisionInDb == null) ? null : PXSelectReadonly<CR.Standalone.CROpportunity,
Where<CR.Standalone.CROpportunity.opportunityID, Equal<Required<CR.Standalone.CROpportunity.opportunityID>>>>.Select(Base, revisionInDb.OpportunityID).FirstOrDefault();
CR.Standalone.CROpportunity opportunity = PXSelect<CR.Standalone.CROpportunity,
Where<CR.Standalone.CROpportunity.opportunityID, Equal<Required<CR.Standalone.CROpportunity.opportunityID>>>>.Select(Base, row.OpportunityID).FirstOrDefault();
var opportunityIsClosed = opportunity?.IsActive == false;
bool allowUpdate = row.IsDisabled != true && !opportunityIsClosed && row.Status != PMQuoteStatusAttribute.Closed;
if (opportunityInDb?.OpportunityID == opportunity?.OpportunityID)
Base.Caches[typeof(PMQuote)].AllowUpdate = allowUpdate;
else
{
var quoteCache = Base.Caches[typeof(PMQuote)];
foreach (var field in quoteCache.Fields)
{
if (!quoteCache.Keys.Contains(field) &&
field != quoteCache.GetField(typeof(PMQuote.opportunityID)) &&
field != quoteCache.GetField(typeof(PMQuote.isPrimary)))
PXUIFieldAttribute.SetEnabled(sender, row, field, allowUpdate);
}
}
PXUIFieldAttribute.SetEnabled<PMQuote.bAccountID>(sender, row, row.OpportunityID == null);
Base.Caches[typeof(PMQuote)].AllowDelete = !opportunityIsClosed;
foreach (var type in new[]
{
typeof(CR.CROpportunityDiscountDetail),
typeof(CR.CROpportunityProducts),
typeof(CR.CRTaxTran),
typeof(CR.CRAddress),
typeof(CR.CRContact),
typeof(CR.CRPMTimeActivity),
typeof(PM.PMQuoteTask)
})
{
Base.Caches[type].AllowInsert = Base.Caches[type].AllowUpdate = Base.Caches[type].AllowDelete = allowUpdate;
}
Base.Caches[typeof(CopyQuoteFilter)].AllowUpdate = true;
Base.Caches[typeof(RecalcDiscountsParamFilter)].AllowUpdate = true;
Base.Actions[nameof(Base.Approval.Submit)]
.SetVisible(row.Status == PMQuoteStatusAttribute.Draft);
Base.actionsFolder
.SetVisible(nameof(Base.Approval.Approve), Base.Actions[nameof(Base.Approval.Approve)].GetVisible());
Base.actionsFolder
.SetVisible(nameof(Base.Approval.Reject), Base.Actions[nameof(Base.Approval.Reject)].GetVisible());
Base.Actions[nameof(EditQuote)]
.SetVisible(row.Status != PMQuoteStatusAttribute.Draft);
Base.Actions[nameof(Base.Approval.Submit)]
.SetEnabled(row.Status == PMQuoteStatusAttribute.Draft && !opportunityIsClosed);
Base.Actions[nameof(Base.Approval.Approve)]
.SetEnabled(row.Status == PMQuoteStatusAttribute.PendingApproval);
Base.Actions[nameof(Base.Approval.Reject)]
.SetEnabled(row.Status == PMQuoteStatusAttribute.PendingApproval);
Base.Actions[nameof(EditQuote)]
.SetEnabled(row.Status != PMQuoteStatusAttribute.Draft && row.Status != PMQuoteStatusAttribute.Closed);
Base.Actions[nameof(Base.CopyQuote)]
.SetEnabled(Base.Caches[typeof(PMQuote)].AllowInsert);
Base.Actions[nameof(PMDiscount.GraphRecalculateDiscountsAction)]
.SetEnabled((row.Status == PMQuoteStatusAttribute.Draft));
Base.Actions[nameof(Base.PrimaryQuote)].SetEnabled(!String.IsNullOrEmpty(row.OpportunityID) && row.IsPrimary == false && row.Status != PMQuoteStatusAttribute.Closed);
Base.Actions[nameof(Base.SendQuote)].SetEnabled(row.Status.IsIn<string>(PMQuoteStatusAttribute.Approved, PMQuoteStatusAttribute.Sent, PMQuoteStatusAttribute.Closed));
Base.Actions[nameof(Base.PrintQuote)].SetEnabled(true);
Base.convertToProject.SetEnabled( (row.OpportunityID == null || row.IsPrimary == true) && row.QuoteProjectID == null && row.Status.IsIn<string>(PMQuoteStatusAttribute.Approved, PMQuoteStatusAttribute.Sent));
PXUIFieldAttribute.SetEnabled<PMQuote.subject>(sender, row, true);
PXUIFieldAttribute.SetEnabled<PMQuote.status>(sender, row, false);
}
}
}
Is it possible to change the default option all column filters? I'm pretty sure I can accomplish this with some JavaScript, but I'd like to know if there's any way within the Acumatica framework to change this.
The answer will be no. The filter is inside the PX.Web.UI.dll in the PXGridFilter class which is an internal class. The property that you are interested in is the Condition.
The value is set inside one of the private methods of the PXGrid class. The code of the method is below:
private IEnumerable<PXGridFilter> ab()
{
List<PXGridFilter> list = new List<PXGridFilter>();
if (this.FilterID != null)
{
Guid? filterID = this.FilterID;
Guid guid = PXGrid.k;
if (filterID == null || (filterID != null && filterID.GetValueOrDefault() != guid))
{
using (IEnumerator<PXResult<FilterRow>> enumerator = PXSelectBase<FilterRow, PXSelect<FilterRow, Where<FilterRow.filterID, Equal<Required<FilterRow.filterID>>, And<FilterRow.isUsed, Equal<True>>>>.Config>.Select(this.DataGraph, new object[]
{
this.FilterID.Value
}).GetEnumerator())
{
while (enumerator.MoveNext())
{
FilterRow row = enumerator.Current;
string dataField = row.DataField;
PXCache pxcache = PXFilterDetailView.TargetCache(this.DataGraph, new Guid?(this.FilterID.Value), ref dataField);
if (this.Columns[row.DataField] != null)
{
List<PXGridFilter> list2 = list;
int valueOrDefault = row.OpenBrackets.GetValueOrDefault();
string dataField2 = row.DataField;
string dataField3 = row.DataField;
int value = (int)row.Condition.Value;
object value2 = pxcache.ValueFromString(dataField, row.ValueSt);
string valueText = row.ValueSt.With((string _) => this.Columns[row.DataField].FormatValue(_));
object value3 = pxcache.ValueFromString(dataField, row.ValueSt2);
string value2Text = row.ValueSt2.With((string _) => this.Columns[row.DataField].FormatValue(_));
int valueOrDefault2 = row.CloseBrackets.GetValueOrDefault();
int? #operator = row.Operator;
int num = 1;
list2.Add(new PXGridFilter(valueOrDefault, dataField2, dataField3, value, value2, valueText, value3, value2Text, valueOrDefault2, #operator.GetValueOrDefault() == num & #operator != null));
}
}
return list;
}
}
}
if (this.FilterRows != null && this.FilterRows.Count > 0)
{
for (int i = 0; i < this.FilterRows.Count; i++)
{
PXFilterRow row = this.FilterRows[i];
list.Add(new PXGridFilter(row.OpenBrackets, row.DataField, row.DataField, (int)row.Condition, row.Value, row.Value.With(delegate(object _)
{
if (this.Columns[row.DataField] == null)
{
return _.ToString();
}
return this.Columns[row.DataField].FormatValue(_);
}), row.Value2, row.Value2.With(delegate(object _)
{
if (this.Columns[row.DataField] == null)
{
return _.ToString();
}
return this.Columns[row.DataField].FormatValue(_);
}), row.CloseBrackets, row.OrOperator));
}
}
return list;
}
UPDATE
The below JS code allows you to change the condition of the last set filter:
px_all.ctl00_phG_grid_fd.show();
px_all.ctl00_phG_grid_fd_cond.items.items[7].setChecked(true);
px_all.ctl00_phG_grid_fd_ok.element.click();
px_all.ctl00_phG_grid_fd_cond.items.items[7].value "EQ"
px_all.ctl00_phG_grid_fd_cond.items.items[8].value "NE"
px_all.ctl00_phG_grid_fd_cond.items.items[9].value "GT"
px_all.ctl00_phG_grid_fd_cond.items.items[13].value "LIKE"
px_all.ctl00_phG_grid_fd_cond.items.items[14].value "LLIKE"
px_all.ctl00_phG_grid_fd_cond.items.items[15].value "RLIKE"
px_all.ctl00_phG_grid_fd_cond.items.items[16].value "NOTLIKE"
px_all.ctl00_phG_grid_fd_cond.items.items[18].value "ISNULL"
px_all.ctl00_phG_grid_fd_cond.items.items[19].value "ISNOTNULL"
I am changing Freight tax category pragmatically based on the tax category of the item selected in the detail tab
protected virtual void SOOrder_CustomerLocationID_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e, PXFieldUpdated del)
{
if (del != null)
del(sender, e);
var row = (SOOrder)e.Row;
Location location = PXSelect<Location, Where<Location.locationID, Equal<Required<Location.locationID>>>>.Select(Base, Base.Document.Current.CustomerLocationID);
if (location != null)
{
string custtaxzone = location.CTaxZoneID;
bool taxableitem = false;
if (custtaxzone == "TAXABLE")
{
foreach (SOLine line in Base.Transactions.Select())
{
if (line.TaxCategoryID == "PATAX")
{
taxableitem = true;
break;
}
}
if (taxableitem)
Base.Document.Current.FreightTaxCategoryID = "PATAX";
else
Base.Document.Current.FreightTaxCategoryID = null;
}
}
}
The value is properly updating but the freight tax is not calculating.
In RowUpdated event, update the DataView after changing the value and invoke calculation of the tax attribute:
public void SOOrder_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
SOOrder row = e.Row as SOOrder;
if (!sender.ObjectsEqual<SOOrder.customerLocationID, SOOrder.customerLocationID>(e.OldRow, e.Row))
{
Base.Document.Current.FreightTaxCategoryID = "PATAX";
Base.Document.Update(Document.Current);
SOOrderTaxAttribute.Calculate<SOOrder.freightTaxCategoryID>(sender, e);
}
}
Invoke the Default attribute to clear the freight tax:
Base.Document.Current.FreightTaxCategoryID = null;
Base.Document.Update(Document.Current);
sender.SetDefaultExt<SOOrder.freightTaxCategoryID>(e.Row);
Problem
How to add some part of data from source excel sheet to the destination excel sheet using Apache POI(XSSF Format)?Excel sheet contains merged columns.
Requirement:
Requirement is not only to copy the row but also to put the data into desired Excel cell(desired column) of the destination sheet.
Note
-Copying row to desired row location in destination excel sheet is achievable. Problem is to first merge the cell as per source sheet in destination sheet and then put data into desired merged excel cell.
- Merged Columns could vary in a row.
Here is the source code, half referred and half written.
public static void copyNodeFrmtSrcToDest(XSSFSheet srcSheet, XSSFSheet destSheet, XSSFRow srcRowStart,XSSFRow srcRowEnd
,XSSFRow destRowStart, int destCellStart, Map<Integer, XSSFCellStyle> styleMap){
/*Check if there is only one row to be pasted*/
int noOfRows = srcRowEnd.getRowNum() - srcRowStart.getRowNum();
/*Check if there is only one row to be pasted*/
if(noOfRows == 0)
{
/*Copy a single row*/
copyRow(srcSheet,destSheet,srcRowStart,destRowStart,destCellStart,styleMap);
return;
}
for (int i = 0;i <= noOfRows ;i++)//For every row
{
/*Get rows from source sheet and increment it*/
XSSFRow srcIntermediateRow = srcSheet.getRow(srcRowStart.getRowNum() + i);
if(destRowStart == null)
{
try {
throw new RowNotFoundError("Row has not been found in the sheet.Kindly create a row.");
} catch (RowNotFoundError e) {
e.printStackTrace();
System.out.println(e.toString());
}
}
if(i!=0)//Assuming destRowStart has been created by user of the API
{
/*Create a new row*/
destRowStart = destSheet.createRow(destRowStart.getRowNum()+1);
}
copyRow(srcSheet,destSheet,srcIntermediateRow,destRowStart,destCellStart,styleMap);
}
}
public static void copyRow(XSSFSheet srcSheet, XSSFSheet destSheet, XSSFRow srcRow, XSSFRow destRow, int destCellStart,
Map<Integer, XSSFCellStyle> styleMap) {
int count = 1;
Set<CellRangeAddress> mergedRegions = new HashSet<CellRangeAddress>();
CellRangeAddress previousMergedRegion =null;
destRow.setHeight(srcRow.getHeight());
for (int j = srcRow.getFirstCellNum(); j <= srcRow.getLastCellNum(); j++) {
int mergedDiff;
XSSFCell oldCell = srcRow.getCell(j);
XSSFCell newCell;
if(j == srcRow.getFirstCellNum()){
newCell = destRow.getCell(destCellStart);}
else
{
newCell = destRow.getCell(destCellStart + count);
}
if (oldCell != null) {
if (newCell == null) {
if(j == srcRow.getFirstCellNum()){
newCell = destRow.createCell(destCellStart);//Keeping the new cell as the first one.
copyCell(oldCell, newCell, styleMap);
}
else{
newCell = destRow.createCell(destCellStart + count);
count = count + 1;
copyCell(oldCell, newCell, styleMap);}
}
CellRangeAddress mergedRegion = getMergedRegion(srcSheet, srcRow.getRowNum(),oldCell.getColumnIndex());
if(previousMergedRegion != null && mergedRegion != null)
{
mergedDiff = mergedRegion.getLastColumn() - mergedRegion.getFirstColumn();
if(!previousMergedRegion.equals(mergedRegion))
{
destCellStart = destCellStart + mergedDiff + 1;
count = 1;
}
}
if (mergedRegion != null) {
previousMergedRegion = mergedRegion.copy();
mergedDiff = mergedRegion.getLastColumn() - mergedRegion.getFirstColumn();
CellRangeAddress newMergedRegion = new CellRangeAddress(destRow.getRowNum(),destRow.getRowNum()
,destCellStart,destCellStart + mergedDiff);
if (isNewMergedRegion(newMergedRegion, mergedRegions))
{
mergedRegions.add(newMergedRegion);
destSheet.addMergedRegion(newMergedRegion);
}
}
}
}
}
public static void copyCell(XSSFCell oldCell, XSSFCell newCell, Map<Integer, XSSFCellStyle> styleMap) {
if(styleMap != null) {
if(oldCell.getSheet().getWorkbook() == newCell.getSheet().getWorkbook()){
newCell.setCellStyle(oldCell.getCellStyle());
} else{
int stHashCode = oldCell.getCellStyle().hashCode();
XSSFCellStyle newCellStyle = styleMap.get(stHashCode);
if(newCellStyle == null){
newCellStyle = newCell.getSheet().getWorkbook().createCellStyle();
newCellStyle.cloneStyleFrom(oldCell.getCellStyle());
styleMap.put(stHashCode, newCellStyle);
}
newCell.setCellStyle(newCellStyle);
}
}
switch(oldCell.getCellTypeEnum()) {
case STRING:
newCell.setCellValue(oldCell.getStringCellValue());
break;
case NUMERIC:
newCell.setCellValue(oldCell.getNumericCellValue());
break;
case BLANK:
newCell.setCellValue(oldCell.getStringCellValue());
break;
case BOOLEAN:
newCell.setCellValue(oldCell.getBooleanCellValue());
break;
case ERROR:
newCell.setCellErrorValue(oldCell.getErrorCellValue());
break;
case FORMULA:
newCell.setCellFormula(oldCell.getCellFormula());
break;
default:
break;
}
}
public static CellRangeAddress getMergedRegion(XSSFSheet sheet, int rowNum, int cellNum) {
for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
CellRangeAddress merged = sheet.getMergedRegion(i);
if (merged.isInRange(rowNum, cellNum)) {
return merged;
}
}
return null;
}
private static boolean isNewMergedRegion(CellRangeAddress newMergedRegion, Set<CellRangeAddress> mergedRegions)
{
if(mergedRegions.isEmpty())
{
return true;
}
return !mergedRegions.contains(newMergedRegion);
}
}
It is working fine for some testcases but not for all.
I just created QT on 28-08-2016 and then change the businessDate to 30-08-2016 and copy it to SOOrder so after copy to SOOrder, I open the QT again and status will change to Completed. But RequestDate on SOLine didn't update.
protected void SOOrder_Status_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (SOOrder)e.Row;
SOOrderExt rowExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(row);
if(row != null)
{
if(row.OrderType == "QT" && row.Status == "C")
{
rowExt.UsrRequestDate = Base.Accessinfo.BusinessDate;
}
else
{
rowExt.UsrRequestDate = row.OrderDate;
}
}
}
protected void SOOrder_OrderDate_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (SOOrder)e.Row;
SOOrderExt rowExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(row);
if(row != null)
{
rowExt.UsrRequestDate = row.OrderDate;
}
}
I think your problem is in the wrong event. You have subscribed to SOLine_RowUpdated, which is event for details. But copy order function will update only document (SOOrder) and will not touch details. You should little bit rethink your flow.
But actually my questions is - why you do not what to use standard logic (as shown on image) that updates requested date for details?
protected void SOOrder_Status_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (SOOrder)e.Row;
SOOrderExt rowExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(row);
if(row != null)
{
if(row.OrderType == "QT" && row.Status == "C")
{
rowExt.UsrRequestDate = Base.Accessinfo.BusinessDate;
}
else
{
rowExt.UsrRequestDate = row.OrderDate;
}
}
}
protected void SOOrder_OrderDate_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (SOOrder)e.Row;
SOOrderExt rowExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(row);
if(row != null)
{
rowExt.UsrRequestDate = row.OrderDate;
}
}