Acumatica - Adding Multiple Tracking Numbers in Field - customization

I am adding the Tracking Number data field from SOPackageDetail to the Invoices screen (SO303000) on the Freight Details Tab. I know that it only shows one ShipmentNbr and this is what I'm using to join the two tables but I would like all of the tracking numbers, since there can be more than one per shipment number, to show in the field instead of just one. They can be just separated in the field value by a comma. Here is my code and it does work for just one tracking number.
Graph:
public class SOInvoiceEntry_Extension : PXGraphExtension<SOInvoiceEntry>
{
#region Event Handlers
protected void SOFreightDetail_RowSelecting(PXCache cache, PXRowSelectingEventArgs e, PXRowSelecting del)
{
if (del != null)
del(cache, e);
var row = (SOFreightDetail)e.Row;
if (row == null) return;
using (new PXConnectionScope())
{
SOPackageDetail track = PXSelect<SOPackageDetail, Where<SOPackageDetail.shipmentNbr, Equal<Required<SOFreightDetail.shipmentNbr>>>>.Select(Base, row.ShipmentNbr);
if(track != null){
SOFreightDetailExt invoiceExt = row.GetExtension<SOFreightDetailExt>();
if (invoiceExt != null){
invoiceExt.TrackNumber = track.TrackNumber;
}
}
}
}
#endregion
}
DAC Extension:
public class SOFreightDetailExt : PXCacheExtension<PX.Objects.SO.SOFreightDetail>
{
#region TrackNumber
public abstract class trackNumber : PX.Data.IBqlField
{
}
protected string _TrackNumber;
[PXString()]
[PXDefault()]
[PXUIField(DisplayName = "Tracking Number", IsReadOnly = true)]
public virtual string TrackNumber
{
get
{
return this._TrackNumber;
}
set
{
this._TrackNumber = value;
}
}
#endregion
}
I want all tracking numbers associated with the Shipment Nbr to be displayed in this field, right now it only shows one. This only will happen if there is multiple packages for one shipment number.

You need to loop on your records (PXSelect) in a foreach. You then need to add each string value to your tracknumber field. Something like this should work...
SOFreightDetailExt invoiceExt = row.GetExtension<SOFreightDetailExt>();
if(invoiceExt == null)
return;
foreach(SOPackageDetail track in PXSelect<SOPackageDetail, Where<SOPackageDetail.shipmentNbr, Equal<Required<SOFreightDetail.shipmentNbr>>>>.Select(Base, row.ShipmentNbr))
{
invoiceExt.TrackNumber = $"{invoiceExt.TrackNumber}, {track.TrackNumber}";
}
Also, there is no need for the PXConnectionScope. You can remove that.

Related

Acumatica - Sales Order add notes per line item

I'm customizing the Sales Order screen in Acumatica to add notes based on the product modifiers from eCommerce, below is the piece of code I grab from previous customization.
I
public virtual void SaveBucketImport(BCSalesOrderBucket bucket, IMappedEntity existing, String operation, SaveBucketImportDelegate baseMethod)
{
MappedOrder order = bucket.Order;
for (int i = 0; i < (order.Extern.OrderProducts?.Count ?? 0); i++)
{
OrdersProductData data = order.Extern.OrderProducts[i];
SalesOrderDetail detail = order.Local.Details.Where(x => x.Delete != true).Skip(i).Take(1).FirstOrDefault();
if(detail != null)
{
// HERE TO ADD NOTES PER LINE
}
}
}
I tried PXNoteAttribute.SetNote(sender, data, option.DisplayName) but still not working
I already debug and try to add PXCache but its seems not working

Acumatica - Action "Reverse and Apply Memo" Default Post Period to active financial period

Can a customization be created to set the post period to the current active financial period after pressing the "Reverse and Apply to Memo" action in "Invoices and Memos" screen?
We've noticed the newly created credit memo defaults to the post period of the invoice which could be incorrect if it's credited in the following financial period.
The solution defined below was developed within Acumatica 20.102.0015 and changes the date and post period for the created credit memo on "Reverse and Apply Memo" action to the default of a new document instead of the date from the reversed invoice.
namespace AARAMPostPeriod
{
public class AAARInvoiceEntryExtension : PXGraphExtension<ARInvoiceEntry>
{
public delegate IEnumerable ReverseDocumentAndApplyToReversalIfNeededDel(PXAdapter adapter, ReverseInvoiceArgs reverseArgs);
[PXOverride]
public virtual IEnumerable ReverseDocumentAndApplyToReversalIfNeeded(PXAdapter adapter, ReverseInvoiceArgs reverseArgs, ReverseDocumentAndApplyToReversalIfNeededDel del)
{
if(reverseArgs.ApplyToOriginalDocument) reverseArgs.DateOption = ReverseInvoiceArgs.CopyOption.SetDefault;
return del(adapter, reverseArgs);
}
}
}
Default value for reverseArgs.DateOption is typically
ReverseInvoiceArgs.CopyOption.SetOriginal
You are talking about the receivables side of things, but I did something similar on the payables side. It isn't exactly what you are asking for, but it is too big for a comment.
You may be able to get the general idea from this and apply to your scenario. The approach I took was to check the period when releasing.
protected virtual void _(Events.FieldUpdated<APInvoice.finPeriodID> e)
{
APInvoice row = (APInvoice)e.Row;
CheckPeriod(e.Cache, row);
}
#region Release override
public delegate IEnumerable ReleaseDelegate(PXAdapter adapter);
[PXOverride]
public virtual IEnumerable Release(PXAdapter adapter, ReleaseDelegate baseMethod)
{
CheckPeriod(Base.Caches[typeof(APInvoice)], Base.Document.Current);
return baseMethod(adapter);
}
#endregion
protected virtual void CheckPeriod(PXCache cache, APInvoice invoice)
{
if (invoice?.FinPeriodID == null) return;
string currentPeriod = GetCurrentPeriod(invoice.BranchID);
if (currentPeriod != invoice.FinPeriodID)
{
PXUIFieldAttribute.SetError<APInvoice.finPeriodID>(cache, invoice, "Invalid period");
}
}
public virtual string GetCurrentPeriod(int? branchID)
{
PXResultset<Branch> Results = PXSelectJoin<GL.Branch,
InnerJoin<FinPeriod, On<FinPeriod.organizationID, Equal<Branch.organizationID>>>,
Where<Branch.branchID, Equal<Required<Branch.branchID>>,
And<FinPeriod.startDate, LessEqual<Required<FinPeriod.startDate>>,
And<FinPeriod.endDate, Greater<Required<FinPeriod.endDate>>>>>> // End Date is the date AFTER the period ends
.SelectSingleBound(Base, null, branchID, Base.Accessinfo.BusinessDate, Base.Accessinfo.BusinessDate);
if (Results != null)
{
foreach (PXResult<GL.Branch, FinPeriod> result in Results)
{
FinPeriod period = result;
return period.FinPeriodID;
}
}
return null;
}
As you can see, I put an override on the Release to perform my validation which sets an error condition if the period is not current. The validation is performed by retrieving the current period of the current business date and comparing to the period on the APInvoice.
You could explore leveraging GetCurrentPeriod from the example and put into an override on FieldDefaulting if it helps with your goal.

Acumatica - field type decimal getting cast error

Customized decimal type field generates error after being published.
I've tried many different syntaxes, if isnull(), etc and for some reason I am not thinking to try the right one.
if (tran != null && tran.TranLineNbr != null &&
arTran != null && arTran.TranType == tran.TranType &&
arTran.RefNbr == tran.RefNbr && arTran.LineNbr == tran.TranLineNbr)
{
decimal? amtOrg = arTran.GetExtension<ARTranExt>().UsrDLYAMTORG;
tran.GetExtension<GLTranExt>().UsrDLYAMTORG = amtOrg;
}
The value on release should go from Ar to Gl. All the other custom fields work, but not the decimal.
"Error: An error occurred during processing of the field Original Amount. Specified cast is not valid."
Your data access class should look similar to
/// <summary>
/// Adds extension fields to and modifies attributes in <see cref="ARTran"/>.
/// </summary>
public sealed class ARTranExt : PXCacheExtension<ARTran>
{
public abstract class usrDLYAMTORG : IBqlField
{
}
[PXDBDecimal]
[PXUIField(DisplayName = "Your Field Name")]
[PXDefault(TypeCode.Decimal, "0.00", PersistingCheck = PXPersistingCheck.Nothing)]
public decimal? UsrDLYAMTORG { get; set; }
}
In regards to bringing a custom value from AR to GL on release we have accomplished the same with the following code.
public class ARReleaseProcessExtension : PXGraphExtension<ARReleaseProcess>
{
public delegate GLTran InsertInvoiceDetailsTransactionDel(JournalEntry je, GLTran tran, ARReleaseProcess.GLTranInsertionContext context);
[PXOverride]
public virtual GLTran InsertInvoiceDetailsTransaction(JournalEntry je, GLTran tran, ARReleaseProcess.GLTranInsertionContext context, InsertInvoiceDetailsTransactionDel del)
{
tran.GetExtension<GLTranExt>().UsrDLYAMTORG = context.ARTranRecord.GetExtension<ARTranExt>().UsrDLYAMTORG;
return del?.Invoke(je, tran, context);
}
}

when I select many row of table and insert breakpoint dont show my list correctly?

when I trace code don't show city list correctly
how I can fix it ?
My code
public ActionResult GetCity(int idCountry)
{
TravelEnterAdminTemplate.Models.LG.MyJsonResult myresult = new Models.LG.MyJsonResult();
var citystable = db.Cities.Where(p => p.CountryId == idCountry).ToList();
if (citystable != null)
{
myresult.Result = true;
myresult.obj = citystable;
}
else
{
myresult.Result = false;
myresult.message = "داده ای یافت نشد";
}
return Json(myresult, JsonRequestBehavior.AllowGet);
}
This is not the error you are getting the data. There were 32 data in cities table . You just click on every node there the detail will visible

pig puzzle: re-writing an involved reducer as a simple pig script?

There are account ids, each with a timestamp grouped by username. foreach of these username groups I want all pairs of (oldest account, other account).
I have a java reducer that does that, can I rewrite it as a simple pig script?
Schema:
{group:(username),A: {(id , create_dt)}
Input:
(batman,{(id1,100), (id2,200), (id3,50)})
(lulu ,{(id7,100), (id9,50)})
Desired output:
(batman,{(id3,id1), (id3,id2)})
(lulu ,{(id9,id7)})
Not that anyone seems to care, but here goes. You have to create a UDF:
desired = foreach my_input generate group as n, FIND_PAIRS(A) as pairs_bag;
And the UDF:
public class FindPairs extends EvalFunc<DataBag> {
#Override
public DataBag exec(Tuple input) throws IOException {
Long pivotCreatedDate = Long.MAX_VALUE;
Long pivot = null;
DataBag accountsBag = (DataBag) input.get(0);
for (Tuple account : accountsBag){
Long accountId = Long.parseLong(account.get(0).toString());
Long creationDate = Long.parseLong(account.get(4).toString());
if (creationDate < pivotCreatedDate ) {
// pivot is the one with the minimal creation_dt
pivot = accountId;
pivotCreatedDate = creationDate;
}
}
DataBag allPairs = BagFactory.getInstance().newDefaultBag();
if (pivot != null){
for (Tuple account : accountsBag){
Long accountId = Long.parseLong(account.get(0).toString());
Long creationDate = Long.parseLong(account.get(4).toString());
if (!accountId.equals(pivot)) {
// we don't want any self-pairs
Tuple output = TupleFactory.getInstance().newTuple(2);
if (pivot < accountId){
output.set(0, pivot.toString());
output.set(1, accountId.toString());
}
else {
output.set(0, accountId.toString());
output.set(1, pivot.toString());
}
allPairs.add(output);
}
}
return allPairs;
}
and if you wanna play real nicely, add this:
/**
* Letting pig know that we emit a bag with tuples, each representing a pair of accounts
*/
#Override
public Schema outputSchema(Schema input) {
try{
Schema pairSchema = new Schema();
pairSchema.add(new FieldSchema(null, DataType.BYTEARRAY));
pairSchema.add(new FieldSchema(null, DataType.BYTEARRAY));
return new Schema(
new FieldSchema(null,
new Schema(pairSchema), DataType.BAG));
}catch (Exception e){
return null;
}
}
}