Solved

How to create Copy of same appointment again in an new action?

  • 14 April 2022
  • 9 replies
  • 446 views

Userlevel 1
Badge

Hello Everyone,

 

I have created a new action button in appointment screen and need to create a new appointment with all the current appointment information includes tabs also in this new action. I have to perform same like copy/paste functionality from this action and have to save the new appointment. How do we perform Copy/paste through logic or is there any other way to do it with acumatica existing methods in easy way instead of creating and assigning everything to a new objects.

Need some suggestions for best possible way to implement this.

Thanks in advance.

 

icon

Best answer by mvolshteyn 11 May 2022, 11:45

View original

9 replies

Userlevel 2
Badge

Hi @rajeshvemunoori31 Try this way, when you click on action button then it will create as a new record with same data.

this is sample code. May be it’s useful.

 

public PXAction<SSPAgreement> revision;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Revision", MapEnableRights = PXCacheRights.Insert,
MapViewRights = PXCacheRights.Insert)]
public virtual IEnumerable Revision(PXAdapter adapter)
{
string msg = "Are you sure you want to create a new revision?";
WebDialogResult result = Agreements.Ask(ActionsMessages.Warning, PXMessages.LocalizeFormatNoPrefix(msg), MessageButtons.OKCancel, MessageIcon.Information, true);

// Update the Active field to False to the Revisioning record.
SSPAgreement sspAgreement = this.Agreements.Current;
sspAgreement.Active = false;
Agreements.Cache.Update(sspAgreement);
Agreements.Update(sspAgreement);
Actions.PressSave();



// Step 1: Create a DAC Object and set its required values.
SSPAgreement CurrentRecord = this.Agreements.Current;
SSPAgreement NewRecord = new SSPAgreement();
NewRecord.TemplateCode = CurrentRecord.TemplateCode;
NewRecord.Revision = (int)CurrentRecord.Revision + 1;
NewRecord.Descr = CurrentRecord.Descr;
NewRecord.Active = true;
NewRecord.AgreeType = CurrentRecord.AgreeType;
NewRecord.AgreeDate = CurrentRecord.AgreeDate;
NewRecord.ExpDate = CurrentRecord.ExpDate;
NewRecord.RefNoteid = CurrentRecord.RefNoteid;
NewRecord.RefNbr = CurrentRecord.RefNbr;
NewRecord.AgreeNbr = CurrentRecord.AgreeNbr;
NewRecord.AgreeRevision = CurrentRecord.AgreeRevision;
NewRecord.Noteid = CurrentRecord.Noteid;

SSPAgreementMaint agreementMaint = PXGraph.CreateInstance<SSPAgreementMaint>();
System.Guid UserIDNumbering = agreementMaint.Accessinfo.UserID;

// Step 2: Insert the newly created DAC into DB.
PXDatabase.Insert<SSPAgreement>(new PXDataFieldAssign(typeof(SSPAgreement.agreeNbr).Name, PXDbType.NVarChar, 15, NewRecord.AgreeNbr),
new PXDataFieldAssign(typeof(SSPAgreement.revision).Name, PXDbType.Int, NewRecord.Revision),
new PXDataFieldAssign(typeof(SSPAgreement.agreeRevision).Name, PXDbType.NVarChar, 30, (NewRecord.AgreeNbr + "-" + NewRecord.Revision)),
new PXDataFieldAssign(typeof(SSPAgreement.agreeType).Name, PXDbType.NVarChar, 5, NewRecord.AgreeType),
new PXDataFieldAssign(typeof(SSPAgreement.templateCode).Name, PXDbType.NVarChar, 30, NewRecord.TemplateCode),
new PXDataFieldAssign(typeof(SSPAgreement.active).Name, PXDbType.Bit, NewRecord.Active),
new PXDataFieldAssign(typeof(SSPAgreement.descr).Name, PXDbType.NVarChar, 256, NewRecord.Descr),
new PXDataFieldAssign(typeof(SSPAgreement.agreeDate).Name, PXDbType.DateTime, NewRecord.AgreeDate),
new PXDataFieldAssign(typeof(SSPAgreement.expDate).Name, PXDbType.DateTime, NewRecord.ExpDate),
new PXDataFieldAssign(typeof(SSPAgreement.refNoteid).Name, PXDbType.UniqueIdentifier, 100, NewRecord.RefNoteid),
new PXDataFieldAssign(typeof(SSPAgreement.refNbr).Name, PXDbType.NVarChar, 30, NewRecord.RefNbr),
new PXDataFieldAssign(typeof(SSPAgreement.createdByID).Name, PXDbType.UniqueIdentifier, 100, UserIDNumbering),
new PXDataFieldAssign(typeof(SSPAgreement.createdByScreenID).Name, PXDbType.Char, 8, "SSP20103"),
new PXDataFieldAssign(typeof(SSPAgreement.createdDateTime).Name, PXDbType.DateTime, System.DateTime.Now),
new PXDataFieldAssign(typeof(SSPAgreement.lastModifiedByID).Name, PXDbType.UniqueIdentifier, 100, UserIDNumbering),
new PXDataFieldAssign(typeof(SSPAgreement.lastModifiedByScreenID).Name, PXDbType.Char, 8, "SSP20103"),
new PXDataFieldAssign(typeof(SSPAgreement.noteid).Name, PXDbType.UniqueIdentifier, 100, agreementMaint.UID),
new PXDataFieldAssign(typeof(SSPAgreement.lastModifiedDateTime).Name, PXDbType.DateTime, System.DateTime.Now));


SSPAgreementMaint graph = PXGraph.CreateInstance<SSPAgreementMaint>();
graph.Agreements.Current = PXSelectOrderBy<SSPAgreement,
OrderBy<Desc<SSPAgreement.createdDateTime>>>.Select(graph);
if (graph.Agreements.Current != null)
throw new PXRedirectRequiredException(graph, "SSP20103");

return adapter.Get();
}
Userlevel 1
Badge

Hello @NageswaraRaoAddanki60 ,

Thank you for the response.

I have tried similar way but it is creating lot of issues in the code. 

 

I have to copy every tab data to create new one. This may miss few columns as well. My actual requirement is to create new appointment in Complete Action. I’m getting another process error here.
 

 public class TestAppointmentEntryExt : PXGraphExtension<AppointmentEntry>
    {
        public static bool IsActive()
        {
            return true;// PXAccess.FeatureInstalled<FeaturesSet.>();
        }


        public delegate IEnumerable CompleteAppointmentDelegate(PXAdapter adapter);
        [PXOverride]
        public IEnumerable CompleteAppointment(PXAdapter adapter, CompleteAppointmentDelegate baseMethod)
        {
            if(Base.AppointmentSelected.Current != null)
            {
                FSAppointmentExt rowExt = Base.AppointmentRecords.Current.GetExtension<FSAppointmentExt>();
                if(rowExt.UsrCompleted == "GB")
                {
                    //FSAppointment AppoldOrderRow = new FSAppointment();

                    FSAppointment AppoldOrderRow = Base.AppointmentRecords.Current;
                    // AppoldOrderRowsele = Base.AppointmentSelected.Current;

                    //if (createAppointment == true)
                    {
                        AppointmentEntry apptGraph = PXGraph.CreateInstance<AppointmentEntry>();

                        FSAppointment appt = new FSAppointment();

                        FSServiceOrder apptserv = new FSServiceOrder();
                        appt.SrvOrdType = AppoldOrderRow.SrvOrdType;
                        appt.SORefNbr = Base.AppointmentRecords.Current.SORefNbr;

                        //appt.CustomerID = AppoldOrderRow.CustomerID;
                        //apptserv.CustomerID = Base.ServiceOrderRelated.Current.CustomerID;
                        //apptserv.BillingBy = Base.ServiceOrderRelated.Current.BillingBy;
                        //appt.billin = AppoldOrderRow.SrvOrdType;
                        //AppoldOrderRow.CopyTo(appt);
                        //apptserv = apptGraph.ServiceOrderRelated.Current = apptGraph.ServiceOrderRelated.Insert(apptserv);
                        appt = apptGraph.AppointmentRecords.Current = apptGraph.AppointmentRecords.Insert(appt);
                        appt.Status = "P";
                        appt = apptGraph.AppointmentRecords.Current = apptGraph.AppointmentRecords.Update(appt);

                        appt = (FSAppointment)apptGraph.AppointmentRecords.Cache.CreateCopy(apptGraph.AppointmentRecords.Current);

                        appt = (FSAppointment)apptGraph.AppointmentSelected.Cache.CreateCopy(Base.AppointmentSelected.Current);
                        appt.HandleManuallyScheduleTime = false;
                        appt = apptGraph.AppointmentSelected.Current = apptGraph.AppointmentSelected.Update(appt);

                        foreach (FSSODet fsSODetRow in Base.ServiceOrderDetails.Select())
                        {
                            var fsAppointmentDetRow = new FSAppointmentDet();

                            fsAppointmentDetRow.ScheduleID = fsSODetRow.ScheduleID;
                            fsAppointmentDetRow.ScheduleDetID = fsSODetRow.ScheduleDetID;
                            //fsAppointmentDetRow = (FSAppointmentDet)apptGraph.AppointmentDetails.Cache.CreateCopy(fsSODetRow);
                            //fsAppointmentDetRow = apptGraph.AppointmentDetails.Current = apptGraph.AppointmentDetails.Insert(fsAppointmentDetRow);
                            AppointmentEntry.InsertDetailLine<FSAppointmentDet, FSSODet>(apptGraph.AppointmentDetails.Cache,
                                                                                         fsAppointmentDetRow,
                                                                                         apptGraph.ServiceOrderDetails.Cache,
                                                                                         fsSODetRow,
                                                                                         fsSODetRow.NoteID,
                                                                                         fsSODetRow.SODetID,
                                                                                         copyTranDate: false,
                                                                                         tranDate: fsSODetRow.TranDate,
                                                                                         SetValuesAfterAssigningSODetID: false,
                                                                                         copyingFromQuote: false);
                        }

                       
                            //FSAppointmentDet apptdet = new FSAppointmentDet();
                            //apptdet = (FSAppointmentDet)apptGraph.AppointmentDetails.Cache.CreateCopy(Base.AppointmentDetails.Select());
                            //apptdet = apptGraph.AppointmentDetails.Current = apptGraph.AppointmentDetails.Insert(apptdet);

                            //apptGraph.AppointmentRecords.SetValueExt<FSAppointment.handleManuallyScheduleTime>
                            //                                     (apptGraph.AppointmentRecords.Current, false);

                        //appt.SORefNbr = Base.AppointmentRecords.Current.SORefNbr;
                        //appt = apptGraph.AppointmentRecords.Current= (FSAppointment)apptGraph.AppointmentRecords.Insert(appt);
                       // appt = (FSAppointment)apptGraph.AppointmentRecords.Cache.CreateCopy(AppoldOrderRow);

                        appt = apptGraph.AppointmentRecords.Current = apptGraph.AppointmentRecords.Update(appt);
                        //foreach (FSAppointmentLog fsSODetRow in Base.LogRecords.Select())
                        //{
                        //    var fsAppointmentDetRow = new FSAppointmentLog();
                        //    fsAppointmentDetRow = (FSAppointmentLog)apptGraph.LogRecords.Cache.CreateCopy(fsSODetRow);
                        //    fsAppointmentDetRow = apptGraph.LogRecords.Current = apptGraph.LogRecords.Insert(fsAppointmentDetRow);
                        //}
                        apptGraph.Actions.PressSave();
                        //appt = apptGraph.AppointmentRecords.Current;
                    }
                }
            }

            return baseMethod(adapter);

        }

Any suggestions….!

 

Thanks,

Userlevel 7
Badge +5

I’d say there are two different approaches that have their pros and cons. 

Approach 1. Copy object in code. 

You can find an example of this approach in CATranEntry.Reverse action.

Here is a generic example: 

//use cache.CreateCopy to duplicate the record
var record= (RecordType)Cache.CreateCopy(Records.Current);
//clear key field values from the record
record.RefNbr=null;
//Insert the record to the cache
Records.Insert(record);

Pros of this approach are

  1. It is efficient in terms of performance
  2. You have full control on all the fields and if you need some additional data manipulation with the object you can easily implement it

Cons: 

  1. Some values of the record may be redefaulted when you insert it, so you need to be aware of the defaulting logic for each field.
  2. If what you are trying to copy is a complex record, e.g. consisting of several DACs, you’ll need to copy each of them and also be aware of the relations between the DACs

Approach 2. Use standard UI Copy-paste functionally.

Here is an example:

// use screenID of the screen that you use to copy data
using (new PXScreenIDScope("IN202500"))
{
InventoryItemMaint graph = PXGraph.CreateInstance<InventoryItemMaint>();

graph.Item.Current = Item;
PXCopyPasteData<InventoryItemMaint> copyPasteData =
new PXCopyPasteData<InventoryItemMaint>();
copyPasteData.CopyFrom(graph);

graph.Item.Current = graph.Item.Insert(new InventoryItem());
copyPasteData.PasteTo(graph);

graph.Save.Press();
}

Pros:

  1. If there is a complex object that needs to be copied, the copy paste functionality takes care of it.
  2. If the copy paste functionality for the screen is already implemented, you reuse the same logic.
  3. You don’t need to write a lot of code.

Cons:

  1. If there is no standard Copy-Paste buttons in UI, you need to implement and test it first.
  2. It’s not that great from the performance point of view.
  3. You don’t have the same level of flexibility of the process.
Userlevel 1
Badge

Hello @Dmitrii Naumov ,

Thanks for the detailed explanation.

I’m trying to use the 2nd approch in appointment screen but i’m getting errors in my code. 

I have used below code.

   public delegate IEnumerable CompleteAppointmentDelegate(PXAdapter adapter);
[PXOverride]
public IEnumerable CompleteAppointment(PXAdapter adapter, CompleteAppointmentDelegate baseMethod)
{
if(Base.AppointmentSelected.Current != null)
{
FSAppointmentExt rowExt = Base.AppointmentRecords.Current.GetExtension<FSAppointmentExt>();
if (rowExt.UsrCompleted == "Test")
{
using (new PXScreenIDScope("FS300200"))
{
AppointmentEntry graph = PXGraph.CreateInstance<AppointmentEntry>();

graph.AppointmentRecords.Current = Base.AppointmentRecords.Current;
//graph.AppointmentSelected.Current = Base.AppointmentSelected.Current;
//graph.ServiceOrderRelated.Current = Base.ServiceOrderRelated.Current;
PXCopyPasteData<AppointmentEntry> copyPasteData =
new PXCopyPasteData<AppointmentEntry>();
copyPasteData.CopyFrom(graph); //Used Base as well here

//graph.ServiceOrderRelated.Current = graph.ServiceOrderRelated.Insert(new FSServiceOrder());
//graph.AppointmentSelected.Current = graph.AppointmentSelected.Insert(new FSAppointment());
graph.AppointmentRecords.Current = graph.AppointmentRecords.Insert(new FSAppointment());
//graph.AppointmentRecords.Current = graph.AppointmentRecords.Insert(new FSAppointment());
copyPasteData.PasteTo(graph);

graph.Save.Press();
}
}
}
return baseMethod(adapter);

}

I’m getting below errors.


The Document summary contains 3 main views( AppointmentRecords, ServiceOrderRelated,AppointmentSelected). I’m trying to use these views as well in the code but getting different errors. 

Am i missing anything here? Please suggest.

Thanks

Userlevel 1
Badge

Hello @Dmitrii Naumov ,

I have tried to implement with the first approach as well, but it is also creating same issue while creating a new appointment. 


AppointmentEntry graph = PXGraph.CreateInstance<AppointmentEntry>();

var record = (FSAppointment)graph.AppointmentRecords.Cache.CreateCopy(Base.AppointmentRecords.Current);
record.SrvOrdType = "MRO";
record.RefNbr = null;
//record.SORefNbr = Base.AppointmentRecords.Current.SORefNbr;
//record.SrvOrdType = Base.AppointmentRecords.Current.SrvOrdType;
graph.AppointmentRecords.Cache.Insert(record); //Getting object reference and fields cannot be empty error here

var recordselected = (FSAppointment)graph.AppointmentSelected.Cache.CreateCopy(Base.AppointmentSelected.Current);
recordselected.RefNbr = null;
graph.AppointmentSelected.Cache.Insert(recordselected);

var recordSrvorder = (FSServiceOrder)graph.ServiceOrderRelated.Cache.CreateCopy(Base.ServiceOrderRelated.Current);
recordSrvorder.RefNbr = null;
graph.ServiceOrderRelated.Cache.Insert(recordSrvorder);
graph.Save.Press();


 

Thanks

Userlevel 7
Badge +5

@rajeshvemunoori31 it seems the appointment screen has some tricks that cause the error. Could you create a support case for that?

Userlevel 7
Badge

Hi @rajeshvemunoori31 have you been able to resolve your issue? Thank you!

Userlevel 5
Badge +3

@rajeshvemunoori31 , please find the code which copies the appointment to a new one (with appointment details, but without creation of a new service order) upon the Complete action

public class VanDykAppointmentEntry_Extension : PXGraphExtension<AppointmentEntry>
{
#region COmplete Action
public virtual void CopyAppointmentBeforeCompletion()
{
AppointmentEntry graph = PXGraph.CreateInstance<AppointmentEntry>();

string sourceOrderType = Base.AppointmentRecords.Current?.SrvOrdType;
string sourceOrderNbr = Base.AppointmentRecords.Current?.RefNbr;
string newOrderType = "MRO ";
string newApptNbr = " <NEW>";


FSAppointment record = (FSAppointment)graph.AppointmentRecords.Cache.CreateCopy(Base.AppointmentRecords.Current);
try
{
graph.SkipLotSerialFieldVerifying = true;
graph.IsCloningAppointment = true;

record.SrvOrdType = newOrderType;
record.RefNbr = null;
record.NoteID = null;
record.AppointmentID = null;
record.SORefNbr = Base.AppointmentRecords.Current.SORefNbr;
record.SOID = Base.AppointmentRecords.Current.SOID;

record = (FSAppointment)graph.AppointmentRecords.Cache.Insert(record); //autosave happens here for some reason

//copy appointment details

PXResultset<FSAppointmentDet> sourceFSApptLines =
PXSelectReadonly<FSAppointmentDet,
Where<FSAppointmentDet.srvOrdType, Equal<Required<FSAppointmentDet.srvOrdType>>,
And<FSAppointmentDet.refNbr, Equal<Required<FSAppointmentDet.refNbr>>>>>
.Select(graph, sourceOrderType, sourceOrderNbr);
foreach (FSAppointmentDet sourceAppointmentDet in sourceFSApptLines)
{
FSAppointmentDet targetAppointmentDet = PXCache<FSAppointmentDet>.CreateCopy(sourceAppointmentDet);
targetAppointmentDet.SrvOrdType = graph.AppointmentRecords.Current.SrvOrdType;
targetAppointmentDet.RefNbr = graph.AppointmentRecords.Current.RefNbr;

// targetAppointmentDet.LineNbr = null;
targetAppointmentDet.AppointmentID = graph.AppointmentRecords.Current.AppointmentID;
targetAppointmentDet.AppDetID = null;
targetAppointmentDet.NoteID = null;
// targetAppointmentDet.EstimatedQty = 0;

graph.Caches[typeof(FSAppointmentDet)].SetValueExt<FSAppointmentDet.estimatedQty>(targetAppointmentDet, 0m);
graph.Caches[typeof(FSAppointmentDet)].SetValueExt<FSAppointmentDet.actualQty>(targetAppointmentDet, 0m);

//targetAppointmentDet = Base.AppointmentDetails.Insert(targetAppointmentDet);
targetAppointmentDet = (FSAppointmentDet)graph.AppointmentDetails.Cache.Insert(targetAppointmentDet);
}

var stamp = PXDatabase.SelectTimeStamp();
PXTimeStampScope.PutPersisted(graph.Caches[typeof(FSAppointment)], graph.AppointmentRecords.Current, stamp);

graph.Save.Press();
}
finally
{
graph.SkipLotSerialFieldVerifying = false;
graph.IsCloningAppointment = false;
}
}

public delegate IEnumerable CompleteAppointmentDelegate(PXAdapter adapter);
[PXOverride]
public IEnumerable CompleteAppointment(PXAdapter adapter, CompleteAppointmentDelegate baseMethod)
{
if (Base.AppointmentSelected.Current != null)
{
CopyAppointmentBeforeCompletion();

}
return baseMethod(adapter);

}
#endregion

}

Notice that this code assigns the  key fields values (SrvOrdType and Refnbr) to the appointment details created (instead of leaving them blank and relying on PXDbDefault)

This happens because the insert operation into the primary (FSAppointment) cache launched during the workflow action inherits the auto-saving behavior for the existing record. Because the Complete Appointment action auto-persists an existing FSAppointment record, this auto-persisting is applied to all other FSAppointment records we operate on during this action.

This reason might explain why you get the error trying t use the ‘generic copy-paste mechanism’ though I did not investigate it deep

Userlevel 5
Badge +3

@rajeshvemunoori31 

Alternatively, you can use the ‘generic Copy-Paste’ approach (

) , but you need to swap the order of base method and custom methods execution

Base method should be executed first, and after this ‘the auto-save’ behavior won’t be observed for FSAppointment inserted to cache

Reply


About Acumatica ERP system
Acumatica Cloud ERP provides the best business management solution for transforming your company to thrive in the new digital economy. Built on a future-proof platform with open architecture for rapid integrations, scalability, and ease of use, Acumatica delivers unparalleled value to small and midmarket organizations. Connected Business. Delivered.
© 2008 — 2024  Acumatica, Inc. All rights reserved