Updating second row on an SOOrder - acumatica

I have an SOOrder with multiple lines and I am trying to update both of the lines and allocations in a loop. I can update the first records split Qty and OrderQty on the line, but when the second line comes along it updates the allocation correctly but the line OrderQty does get changed. If I do this same logic on an order with just one line it works as expected.
protected void _(Events.RowPersisted<INLotSerialStatus> e)
{
var soOrderEntryMaint = PXGraph.CreateInstance<SOOrderEntry>();
var existingSHSoLineLineSplits = soOrderEntryMaint.Select<SOLineSplit>()
.Where(w => w.OrderType == SSIConstants.OrderTypes.SH &&
w.LotSerialNbr == lotSerial &&
w.SiteID == siteID)
.OrderBy(o => o.Qty)
.ToList();
// 1) loop through each split and get the SoOrder and the SoLine
foreach (SOLineSplit soLineSplit in existingSHSoLineLineSplits)
{
if (remaining <= 0) return;
// 2) set SoOrder and SOLine SoLineSpLit as current
soOrderEntryMaint.splits.Current = soLineSplit;
var soOrderNumber = soOrderEntryMaint.Select<SOOrder>()
.First(w => w.OrderType == SSIConstants.OrderTypes.SH &&
w.OrderNbr == soLineSplit.OrderNbr);
soOrderEntryMaint.Document.Current = soOrderNumber;
var soLine = soOrderEntryMaint.Select<SOLine>()
.First(w => w.OrderType == SSIConstants.OrderTypes.SH &&
w.OrderNbr == soLineSplit.OrderNbr &&
w.LineNbr == soLineSplit.LineNbr);
soOrderEntryMaint.Transactions.Current = soLine;
// 3) decrement SOLineSplit
// 4) decrement the SoLine
// 5) decrement remaining
if (soLineSplit.Qty > remaining)
{
soLineSplit.Qty -= remaining;
soLine.Qty -= remaining;
remaining = 0;
}
else if (soLineSplit.Qty <= remaining)
{
var qtyToRemove = soLineSplit.Qty;
soLine.Qty = soLine.Qty - qtyToRemove <= 0
? 0
: soLine.Qty - qtyToRemove;
soLineSplit.Qty = 0;
remaining -= qtyToRemove ?? 0 ;
}
// 6) update and save.Press
// 7) Maint clear
soOrderEntryMaint.splits.Update(soLineSplit);
soOrderEntryMaint.Transactions.Update(soLine);
soOrderEntryMaint.Actions.PressSave();
soOrderEntryMaint.Clear();
}
}

It looks like your Save and Clear actions are in the for loop. Maybe if you move them outside this will work correctly?
After the first iteration, the code is saving and clearing the SOOrderEntry graph.

I went further into the code and I found that an existing update event was found that was canceling further processing. The first one would always work and when that Cancel was called it was not being removed. I ended up moving the "var soOrderEntryMaint = PXGraph.CreateInstance(); " to the end of the loop so that way any events that were added will not be there on the next item. May not be the prettiest way but it works.

Related

Trouble with indices

I am writing a Maximum Value Knapsack algorithm. It takes in a Knapsack object with Items that have a value and cost. I declare a 2D array for calculating the max value. For the base cases I have set the zeroth row values to 0 and zeroth column values to 0. I am running into trouble when I grab an item in the knapsack because when I want to grab the zeroth item, I am really grabbing the first item in the knapsack and am consequently getting the wrong values in the 2D array. Can someone check out my code and see what I am missing?
public static double MaximumKnapsack(Knapsack knapsack) {
int numItems = knapsack.getNumOfItems();
int budget = (int) knapsack.getBudget();
double[][] DP = new double[numItems+1][budget+1];
boolean taken = false;
for (int i = 0; i < numItems + 1; i++) {
for (int b = 0; b < budget + 1; b++) {
if (i == 0 || b == 0) {
DP[i][b] = 0;
}
else
{
Item item = knapsack.getItem(i);
if (item.getCost() > b) {
DP[i][b] = DP[i-1][b];
}
else
{
DP[i][b] = Math.max(DP[i-1][b-(int) item.getCost()] + item.getValue(),
DP[i-1][b]);
if (DP[i][b] == DP[i-1][b-(int) item.getCost()] + item.getValue() && item.getCost() != 0.0) {
taken = true;
}
}
}
}
taken = false;
}
return DP[numItems][budget];
}
I think the problem is in
Item item = knapsack.getItem(i);
beacuse your loop will start with i = 1. You should use:
Item item = knapsack.getItem(i-1);

LongAdder Striped64 wasUncontended implementation detail

This is a question not about how LongAdder works, it's about an intriguing implementation detail that I can't figure out.
Here is the code from Striped64 (I've cut out some parts and left the relevant parts for the question):
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
int h;
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current(); // force initialization
h = getProbe();
wasUncontended = true;
}
boolean collide = false; // True if last slot nonempty
for (;;) {
Cell[] as; Cell a; int n; long v;
if ((as = cells) != null && (n = as.length) > 0) {
if ((a = as[(n - 1) & h]) == null) {
//logic to insert the Cell in the array
}
// CAS already known to fail
else if (!wasUncontended) {
wasUncontended = true; // Continue after rehash
}
else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x)))){
break;
}
A lot of things from code are clear to me, except for the :
// CAS already known to fail
else if (!wasUncontended) {
wasUncontended = true; // Continue after rehash
}
Where does this certainty that the following CAS will fail?
This is really confusing for me at least, because this check only makes sense for a single case : when some Thread enters the longAccumulate method for the n-th time (n > 1) and the busy spin is at it's first cycle.
It's like this code is saying : if you (some Thread) have been here before and you have some contention on a particular Cell slot, don't try to CAS your value to the already existing one, but instead rehash the probe.
I honestly hope I will make some sense for someone.
It's not that it will fail, it's more that it has failed. The call to this method is done by the LongAdder add method.
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
longAccumulate(x, null, uncontended);
}
}
The first set of conditionals is related to existence of the long Cells. If the necessary cell doesn't exist, then it will try to accumulate uncontended (as there was no attempt to add) by atomically adding the necessary cell and then adding.
If the cell does exist, try to add (v + x). If the add failed then there was some form of contention, in that case try to do the accumulating optimistically/atomically (spin until successful)
So why does it have
wasUncontended = true; // Continue after rehash
My best guess is that with heavy contention, it will try to give the running thread time to catch up and will force a retry of the existing cells.

NetSuite - OnValidateLine not working

My validate line function below appears to only work on the second time of clicking add. So if the amount is 500 and I set the discount to 100, on hitting the add button it should be 400. It doesn't work. If I click the line again, the it seems the discount applies twice - amount becomes 300. How do I resolve this?
function OnValidateLine(type, name) {
var count = nlapiGetLineItemCount('expense')
var total = nlapiGetFieldValue('usertotal')
for (var x = 1; x <= count; x++) {
var amount = nlapiGetCurrentLineItemValue('expense', 'amount');
var discount = nlapiGetCurrentLineItemValue('expense', 'custcolptc_discount');
if (discount) {
nlapiSetCurrentLineItemValue('expense', 'amount', amount - discount)
}
return true;
}
return true;
}
You are not actually doing anything with your loop.
Also the way you are going about this is fraught with issues.
You should probably do this in the recalc event rather than a line validation event.
If I were doing this I'd tend manage a 'synthetic' expense line that is the total of calculated discounts. The way you are currently doing this if someone changes the expense description you'll end up with the discount applied twice. If you use a discount line then you'll just total up the discounts and add or update the discount line.
Typically for a Client script you would need to advance the pointer for each line you are looking at. In the untested example below the 'id field?' would be memo or account columns (you'll have to set the account anyway):
var totalDiscount = 0;
var discountExpenseAt = 0;
for(var i = nlapiGetLineItemCount('expense'); i> 0; i--){
if(nlapiGetLineItemValue('expense', 'id field?', i) == 'discount identifier') {
discountExpenseAt = i;
continue;
}
totalDiscount += parseFloat(nlapiGetLineItemValue('expense', 'custcolptc_discount', i)) ||0;
}
if(totalDiscount) {
if(discountExpenseAt){
nlapiSelectCurrentLineItem('expense', discountExpenseAt);
nlapiSetCurrentLineItemValue('expense', 'amount', totalDiscount.toFixed(2));
nlapiSetCurrentLineItemValue('expense', 'id field?', 'discount identifier');
nlapiCommitCurrentLineItem('expense');
}

Foreach loop per item fill document

I have got a list and I want to go through each item condition and insert it into a document. The issue is that I don’t know how to find an item and go through each condition till the item changes then start again.
Item Condition Total
Bag New 3
Bag Old 5
Jacket New 2
Racket New 1
Racket old 3
Racket unknown 8
This is what I do:
foreach (DataGridViewRow row in tracker.dataGridView1.Rows)
{
if (row.Cells[0].Value != null)
{
string template = #"C:\ document.pdf";
string newFile = #"c:\"+ row.Cells[0].Value.ToString() +".pdf";
PdfReader pdfReader = new PdfReader(pdfTemplate);
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(newFile, FileMode.Create));
AcroFields pdfFormFields = pdfStamper.AcroFields;
******************************************************************************
// set values for fields
if (row.Cells[1].Value.ToString() == "New")
{
pdfFormFields.SetField("Condition", row.Cells[2].Value.ToString());
}
else if (row.Cells[1].Value.ToString() == "Old")
{
pdfFormFields.SetField("Condition2", row.Cells[2].Value.ToString());
}
***************************************************************************************
pdfStamper.FormFlattening = true;
pdfStamper.Close();
}
else
break;
}
I want the code between the stars to fill the same document till row.cells[0].value changes then start the foreach again.
Just store the value of row.cells[0].value in a variable and compare it during each iteration of the foreach loop.
You also have to distinguish the first row from the rest, since only then a comparison of two values makes sense.
Example code:
string previousValue = null;
string currentValue = null;
bool firstRow = true;
foreach (DataGridViewRow row in tracker.dataGridView1.Rows)
{
currentValue = row.Cells[0].Value.ToString();
if (currentValue != null)
{
string template = #"C:\ document.pdf";
string newFile = #"c:\"+ currentValue +".pdf";
PdfReader pdfReader = new PdfReader(pdfTemplate);
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(newFile, FileMode.Create));
AcroFields pdfFormFields = pdfStamper.AcroFields;
// Compare previous with current value
if (!firstRow && previousValue != currentValue)
{
// Difference, start new document
....
}
else
{
// Same Value
}
// When row is handled, update previous with current value, and set firstRow to false
previousValue = currentValue;
firstRow = false;
...

How to use the repaint method

I am trying to use the repaint method in the following code to update the screen after user input. The game is a card game where the user has to click on two cards to reveal their pictures. If the pictures match the cards remain visible however if the pictures don't match the cards flip over to hide the pictures once again.
The first card becomes visible after clicking it, however when the second card is selected either both cards become visible if a matching picture is selected or the first card just flips over without the second picture being revealed.
Thanks for your help.
addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e) {
int row = e.getX() / (Card.SIZE*2);
int col = e.getY() / (Card.SIZE*3);
//OPEN means the picture is visible
if(cards[row][col].getState() == Card.CLOSED)
cards[row][col].setState(OPEN);
repaint();
compareCards(row,col);
}
});
}
public void compareCards(int row, int col){
if(clickNum == 1){
r1 = row;
c1 = col;
clickNum++;
}
else if(clickNum == 2){
r2 = row;
c2 = col;
//The OR accounts for clicking twice on the same tile
if(cards[r1][c1].getNum() != cards[r2][c2].getNum() || (r1 == r2 && c1 == c2)){
cards[r1][c1].setState(CLOSED);
cards[r2][c2].setState(CLOSED);
}
clickNum = 1;
}
}
Your compare cards function is setting the card states to CLOSED much too quickly, and so they are not displaying. Try using:
public void compareCards(int row, int col){
try
{
Thread.sleep(5000);//sleep for five seconds
}catch(Exception e){}
if(clickNum == 1){
r1 = row;
c1 = col;
clickNum++;
}
else if(clickNum == 2){
r2 = row;
c2 = col;
//The OR accounts for clicking twice on the same tile
if(cards[r1][c1].getNum() != cards[r2][c2].getNum() || (r1 == r2 && c1 == c2)){
cards[r1][c1].setState(CLOSED);
cards[r2][c2].setState(CLOSED);
}
clickNum = 1;
}
}
This should display both cards for about five seconds before turning them over. You'll also have to implement a method which handles flipping the cards back over if they are the same if you don't already have one. I only say this because I don't see one here.

Resources