Hello friends I need help:
Here I specify what I am doing:
1.- I am overriding the release method of Employee Timecard, do a validation to get the project's default branch code, and then insert it into the project's transaction screen.
public class TimeCardMaint_Extension : PXGraphExtension<TimeCardMaint>
{
#region Event Handlers
public delegate IEnumerable ReleaseDelegate(PXAdapter a);
[PXOverride]
public IEnumerable Release(PXAdapter adapter, ReleaseDelegate InvokeBase)
{
PXGraph.InstanceCreated.AddHandler<RegisterEntry>((graph) =>
{
graph.RowInserted.AddHandler<PMTran>((sender, e) =>
{
EPTimecardDetail detail = PXResult<EPTimecardDetail>.Current;
if (detail != null)
{
var tran = (PMTran)e.Row;
PMProject project = PXSelect<PMProject,
Where<PMProject.contractID, Equal<Required<PMProject.contractID>>>>.Select(Base, detail.ProjectID);
if (project != null)
{
tran.BranchID = project.DefaultBranchID;
}
}
});
});
return InvokeBase(adapter);
}
#endregion
}
Here we see the Transactions screen of the project, make the correct change.
So far everything perfect:
However, if I check the Journal Transactions screen, it has generated two new entries, it should really only generate a single journal entry as it does by default in acumatica.
Due to these consequences, it is because I have modified the employee's time card, in the release method, I don't know what is happening:
I need you to tell me what I should do or what I am doing wrong, really I only have to modify the Project Transactions screen and the others should not affect I hope I have been clear..
Digging through the code, the release function for the Project Transactions groups them together by branch.
This is executed at each entry:
Batch newbatch = je.BatchModule.Insert(new Batch
{
Module = doc.Module,
Status = BatchStatus.Unposted,
Released = true,
Hold = false,
BranchID = branchID,
FinPeriodID = parts[1],
CuryID = parts[2],
CuryInfoID = info.CuryInfoID,
Description = doc.Description
});
As you can see, it adds to the batch of the branch. This would filter out one document for each type, even though it could be separate on the line:
Since you are having one time card, and then two branches, it would post one time card to one branch, then the other time card to the other branch. If you were to change the line item for each, you would have to override the entire release functions. So based on spending time combing through the project code, and knowing the GL processes, it seems to be operating as intended for acumatica.
Now to your point to bring it into one, I would override the Release function of the PX.Objects.PM.RegisterReleaseProcess graph:
public virtual List<Batch> Release(JournalEntry je, PMRegister doc, out List<PMTask> allocTasks)
You would make a copy of the entire function, and then create one batch, and then override the transaction. The document would still be tagged with the employee's primary branch, but each line would then be updated to the proper branch.
NOTE: This would need to be tested thoroughly each update to Acumatica. You would also need to verify from the accounting side that all of the reporting is correct. Some companies may report on the document's branch rather than the transactions, or ignore it and look at the account that it is hitting altogether.
I hope this helps get you to the desired solution!
Related
I have inherited a Windows Forms Application. We are having some performance issues, when trying to save a new record to the SQL Table. It hangs.
For the most part, I have ruled out the database or Table Structure as the problem. I don't believe that is the case here.
I am very new to this, and am having trouble stepping through and finding the root of the problem.
The form basics: (Only included what I thought was relevant code, but can add more if needed)
public partial class frmBadgeCreate : daBaseForm
{
private boBadger_History oBadger_History;
Form mainFormHandler;
private string whome;
}
public frmBadgeCreate(String cutomItem)
{
InitializeComponent();
if (daAppDesktop.IsRunning)
{
oBadger_History = new boBadger_History();
oBadger_History.GetAll(); /// This line right here seems to have some importance
whome = customItem;
}
}
public void saveitall() /// Save Action that hangs
{
// listing of form variables to be saved to table columns
var vlast = textbox_H_lname.Text;
var vfirst = textbox_H_fname.Text;
. . .and on and on . . .
var badger_History = new Badger_History() {hlastname = vlast, vfirstname = vfirst . . . and on and on . . };
oBadger_History.Add(badger_History);
oBadger_History.Save(); /// This is where things just hang forever.
Because this is a 'Lone Ranger App' that was handed to me, I am struggling to grasp it. What really confuses me is that when I comment out the 'oBadger_History.GetAll()' line, the save works very fast! Instantly. When I add the line back in, it hangs. I only know this, because I have spent days commenting out each line, one by one, and testing results.
The oBadger_History.GetAll(); looks like it is somehow used for the auto complete feature, so it is needed.
What has me totally scratching my head is that I can't see the connection between the 'GetAll' and the save. Why would the getAll impact the save function at all?
Here is the GetAll code, if that sheds any light:
public daBindingList<Badger_History> GetAll()
{
BadgerContext cn = (BadgerContext)this.context;
List<Badger_History> ents = cn.Badger_History.ToList();
this.EntityList = new daBindingList<Badger_History>(ents);
return this.EntityList;
}
Again, I don't believe that the SQL database/tables are the problem, seeing that I can get the save to work properly by removing the one line of code. I just can't seem to find a way to resolve
This line:
List<Badger_History> ents = cn.Badger_History.ToList();
Will load EVERY row in the Badger_History table into memory. How many records are in this table?
If there are lots of rows, then when you call Save(); (which I assume is some sort of wrapper around SaveChanges(), then EF will look through every row for anything that has changed. In your case, there may be 0 rows changed, as all you are interested in, is the row you are adding.
To speed things, you could change loading the data into a 'non-tracking' query
List<Badger_History> ents = cn.Badger_History.AsNoTracking().ToList();
This will still load in all the records, but they will no longer be counted when trying to save.
I am trying to create a process that will run daily that will import records from another database as Inventory Items. In order to do this, I need to create an extension of the InventoryItemMaint graph (to give me my custom action), as well as an extension of the InventoryItem DAC (to give me a custom field). I have tried to follow the guidelines laid out specifically in the T-300 manual to do this.
Here is the code for my InventoryItemMaint extension:
namespace PX.Objects.IN
{
public class InventoryItemMaint_Extension:PXGraphExtension<InventoryItemMaint>
{
public PXAction<PX.Objects.IN.InventoryItem> DailyOnixImport;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Daily Onix Import")]
protected void dailyOnixImport()
{
var invItemMaintExtInstance = Base.GetExtension<InventoryItemMaint_Extension>();
string todaysDate = DateTime.Today.ToString("MM/dd/yyyy");
foreach (STOnixItem currentOnixItem in PXSelect<STOnixItem,
Where<STOnixItem.addedDate, Equal<Required<STOnixItem.addedDate>>>>
.Select(this.Base, todaysDate))
{
InventoryItem currentInventoryItem = invItemMaintExtInstance.Base.Item.Current;
PXCache inventoryItemCache = invItemMaintExtInstance.Base.Item.Cache;
InventoryItemExt inventoryItemExtension = inventoryItemCache.GetExtension<InventoryItemExt>(currentInventoryItem);
inventoryItemCache.Clear();
currentInventoryItem.InventoryCD = currentOnixItem.ISBN13;
currentInventoryItem.Descr = currentOnixItem.Title;
currentInventoryItem.ItemClassID = currentOnixItem.ItemClass;
currentInventoryItem.RecPrice = decimal.Parse(currentOnixItem.MSRP);
currentInventoryItem.BasePrice = decimal.Parse(currentOnixItem.DefaultPrice);
currentInventoryItem.BaseItemWeight = decimal.Parse(currentOnixItem.Weight);
currentInventoryItem.WeightUOM = "POUND";
currentInventoryItem.ImageUrl = currentOnixItem.ImageLink;
//Assigning to the custom DAC Extension
inventoryItemExtension.UsrFromOnixFile = currentOnixItem.FromFile;
inventoryItemCache.Update(currentInventoryItem);
Base.Actions.PressSave();
}
}
}
}
I am currently getting an error that reads:
Error: Another process has updated the 'InventoryItem' record. Your
changes will be lost.
And here is the error trace text:
9/20/2018 3:26:05 PM Error: Error: Another process has added the
'InventoryItem' record. Your changes will be lost.
at PX.Data.PXCache1.PersistInserted(Object row) at
PX.Data.PXCache1.Persist(PXDBOperation operation) at
PX.Data.PXGraph.Persist(Type cacheType, PXDBOperation operation)
at PX.Data.PXGraph.Persist() at
PX.Objects.IN.InventoryItemMaint.Persist() at
PX.Data.PXSave1.d__2.MoveNext() at
PX.Data.PXAction1.d__31.MoveNext() at
PX.Data.PXAction1.d__31.MoveNext() at
PX.Data.PXActionCollection.PressSave(PXAction caller) at
PX.Objects.IN.InventoryItemMaint_Extension.dailyOnixImport() at
PX.Data.PXAction1.<>c__DisplayClass3_0.<.ctor>b__0(PXAdapter adapter)
at PX.Data.PXAction1.a(PXAdapter A_0) at
PX.Data.PXAction1.d__31.MoveNext() at
PX.Data.PXAction`1.d__31.MoveNext() at
PX.Web.UI.PXBaseDataSource.tryExecutePendingCommand(String viewName,
String[] sortcolumns, Boolean[] descendings, Object[] searches,
Object[] parameters, PXFilterRow[] filters, DataSourceSelectArguments
arguments, Boolean& closeWindowRequired, Int32& adapterStartRow,
Int32& adapterTotalRows) at
PX.Web.UI.PXBaseDataSource.ExecuteSelect(String viewName,
DataSourceSelectArguments arguments, PXDSSelectArguments pxarguments)
I have done a lot of searching around StackOverflow and other places, but haven't found any answers that seem to address my issue exactly. Tweaks I've made have resulted in other errors like variations on what I'm getting now (another process added vs another process updated) and MoveNext errors.
If anyone is able to help me out, I would be very appreciative.
guys,
I faced the same exception in the EmployeeMaint when we tried to update one of the complex fields in the DAC.
baseAction() throws this exception even though no code runs before saving.
I followed your suggestions #Hugues Beauséjour and found out that reason may be:
Means, the cache is dirty.
So, to resolve my issue I just needed to clear the cache for the object that throws the exception.
IN my case the exception was:
Update employee class error - Another process has added the 'VendorPaymentMethodDetail' record. Your changes will be lost
So I needed to clear the cache for VendorPaymentMethodDetail:
Base.Caches<VendorPaymentMethodDetail>().Clear();
Be careful to clear the cache in the case you need to read from the cache during your code after you clear the cache. In that case, you need to copy objects from the cache before cleaning and used this copy afterwards.
Hope it will be helpful to someone.
There seem to be a logical flaw in the code. You are updating the same current object in a loop. This serve no purpose as it will always overwrite with the last item returned by the loop. Invoking the Save action in a loop can also lead to errors if you're not careful.
As I mentioned in the comment, clearing the cache seems wrong. You want to keep the current data there. When you call clear you're invalidating the main document of the graph, that will lead to errors.
Changing fields closely tied to the key like InventoryCD can lead to the document being cleared and invalidated. If you have to modify key fields considering inserting new record instead of updating the current one.
There are other changes I would recommend.
Code:
// Consider replacing the default namespace to avoid conflicts
namespace MyNamespace
{
public class InventoryItemMaint_Extension:PXGraphExtension<InventoryItemMaint>
{
public PXAction<PX.Objects.IN.InventoryItem> DailyOnixImport;
// '(CommitChanges = true)' is not necessary
[PXButton]
[PXUIField(DisplayName = "Daily Onix Import")]
protected void dailyOnixImport()
{
InventoryItemMaint_Extension invItemMaintExtInstance = Base.GetExtension<InventoryItemMaint_Extension>();
string todaysDate = DateTime.Today.ToString("MM/dd/yyyy");
// You need to rethink that 'foreach' logic
STOnixItem currentOnixItem in PXSelect<STOnixItem,
Where<STOnixItem.addedDate, Equal<Required<STOnixItem.addedDate>>>>.Select(Base, todaysDate);
// You can access Base directly, no need to fetch it from the extension
InventoryItem currentInventoryItem = Base.Item.Current;
// Consider using more null check
if (currentOnixItem != null && currentInventoryItem != null)
{
// Consider using similar names for similar variables
InventoryItemExt currentInventoryItemExt = currentInventoryItem.GetExtension<InventoryItemExt>();
// Avoid setting key related fields like InventoryCD when updating
currentInventoryItem.Descr = currentOnixItem.Title;
currentInventoryItem.ItemClassID = currentOnixItem.ItemClass;
currentInventoryItem.RecPrice = decimal.Parse(currentOnixItem.MSRP);
currentInventoryItem.BasePrice = decimal.Parse(currentOnixItem.DefaultPrice);
currentInventoryItem.BaseItemWeight = decimal.Parse(currentOnixItem.Weight);
currentInventoryItem.WeightUOM = "POUND";
currentInventoryItem.ImageUrl = currentOnixItem.ImageLink;
currentInventoryItemExt.UsrFromOnixFile = currentOnixItem.FromFile;
// You fetched the item from the DataView
// you can update it in the DataView too.
Base.Item.Update(currentInventoryItem);
// Is it really needed to save here?
// This coupled with cache clearing and the loop updating
// the same record triggers the error in your question.
Base.Actions.PressSave();
}
}
}
}
I found another reason that can cause the same exception:
That may happen when you select from the same table in different caches, use one for select and another for insert. for instance:
we have a view:
public PXSelect<MPEmployeeWorkSchedule, Where<MPEmployeeWorkSchedule.employeeID, Equal<Current<MPEmployeeTermination.employeeID>>>> EmployeeWorkSchedule;
and in event we have code's segment:
we use the same DAC as in the view above and then we insert into the view:
EmployeeWorkSchedule.Cache.Insert(workSchedule);
Afterwards Persist() throws exception.
Solution was again to create another view instead of query and clear the cache:
This may be helpful, I hope.
Want to share one more way of finding the same error message. In my case the problem was in structure of database.
In order to fix the error message, I've did those steps:
dropped my table
generated sql script from existing table
deleted not needed columns
copy/pasted existing columns, with replacing names
As outcome, I've got error message to disappear.
I have a Orchard CMS running that is tied to a user synchronization. This sync updates each user overnight and the code begins with
... = mContentManager.Get<Orchard.Users.Models.UserPart>(lOrchardUser.ContentItem.Id,
Orchard.ContentManagement.VersionOptions.DraftRequired);
As you can see VersionOptions.DraftRequired is passed to the Get() method with creates a new draft each time the user is synchronized. It's not intended to create a new draft here so i changed it to VersionOptions.Published which avoids creating a new version record on each call.
But issue here is that passing VersionOptions.DraftRequired in the past has created around 120 version records for each user whereas there are around 1000 users in the DB.
When i now use IContentManager.Query() it takes considerably longer due to the high amount of versions.
My idea is to remove all versions except the published one as i don't need them but IContentManager doesn't provide any version removal option and deleting the records by using IRepository<> causes a NHibernate exception.
So my last resort was to use LINQ queries to remove the versions, this did work but i was told that using LINQ in Orchard is not recommended.
I wonder whether anyone had any issues with high amounts of version records as the longer the system runs the more data is accumulated and the system is getting slower and slower.
So, my questions are
is there an Orchard way to remove version records?
is there a way to disable versioning?
As there seems no Orchard way to do this, i deleted the versions with the following LINQ 2 SQL code:
public System.Web.Mvc.ActionResult RemoveVersions()
{
// select user ids, an alternate way is to retrieve user IDs via content manager but this takes a veeeeery long time due
// to the need of querying all versions
//
// var lOrchardUserIDs = mContentManager
// .Query<Orchard.Users.Models.UserPart, Orchard.Users.Models.UserPartRecord>(Orchard.ContentManagement.VersionOptions.AllVersions)
// .List()
// .Select(u => u.Id)
// .ToList()
var lOrchardUserIDs = mUserRepository.Fetch(u => true).Select(u => u.Id).ToList();
foreach (var lOrchardUserID in lOrchardUserIDs)
{
var lContentItemVersionRecords =
(from r in DataContext.ContentItemVersionRecords where r.ContentItemRecord_id == lOrchardUserID select r).ToList();
if (lContentItemVersionRecords.Count > 1)
{
foreach (var lContentItemVersionRecord in lContentItemVersionRecords)
{
if (lContentItemVersionRecord.Number == 1)
{
if (lContentItemVersionRecords[lContentItemVersionRecords.Count - 1].Published)
{
lContentItemVersionRecord.Latest = true;
lContentItemVersionRecord.Published = true;
}
}
else
DataContext.ContentItemVersionRecords.DeleteOnSubmit(lContentItemVersionRecord);
}
}
}
DataContext.SubmitChanges();
return Content("Done");
}
and sorry for a poor title of the question
I´m trying to get all free rooms (not booked ones) from my exchange server. The thing is that I also get the rooms I have booked but nobody has accepted.
I would like to exclude them from the list after I book the room.
This is the code I use to book a room
ExchangeService service;
//...code to new-up and config service
var request = new Appointment(service)
{
Subject = booking.Subject,
Start = booking.Start,
End = booking.End,
Location = booking.Room
};
request.RequiredAttendees.Add(booking.Person);
request.RequiredAttendees.Add(booking.Room);
request.Save(SendInvitationsMode.SendOnlyToAll);
To note I have tried to call request.Accept() straight after Save() but without that "realy booking" the room. Pressing accept in Outlook is the only "fix". Needlessly to say I have tried everything I could find about this issue (I do not work with Exchange regularly).
And then the code to get free rooms
var rooms = service.GetRooms(locationAddress);
// all the meeting rooms at location
var rooms= rooms.Select(i => new AttendeeInfo { SmtpAddress = i.Address, AttendeeType = MeetingAttendeeType.Room });
// Get all availabilites from all rooms at given locations
var availability = service.GetUserAvailability(rooms, timeframe, AvailabilityData.FreeBusy);
foreach (var a in availability.AttendeesAvailability)
{
// Here we always get all the free rooms
// including the ones we booked earlier
// UNTIL somebody clicks accept in Outlook and then it does not appear here!?
}
I can´t see that the rooms are marked differently before they are accepted in Outlook so I can´t differentiate between them and pull out them ones I don´t want.
I also really think that those rooms should not be available so there must bee some enum/tick/mark I can put on the room before booking it but I totally missing it.
Edit
I realy don't understand why there is no option for AvailabilityData.Free in the enum in the GetUserAvailability method (only FreeBusy, FreeBusyAndSuggestions and Suggestions but no Free only!?)
Ok this is rather silly... the code (I inherited) did not include code to actually remove rooms that where taken (busy). I only just noticed this...
The code to fix this is simply this one
var busyRoomToRemove = a.CalendarEvents.ToList().Find(x => x.FreeBusyStatus == LegacyFreeBusyStatus.Busy);
a.CalendarEvents.Remove(busyRoomToRemove);
Sorry for the inconvenience :-)
As a preface, I've looked at every StackOverflow question matched by searching for this error (25 of them or so), and none of them seemed to address the problem I'm having.
I'm building up a PermissionsDialog that inherits from System.Windows.Form. Within the method that calls dialogPermissions.ShowDialog() I am retrieving some Role objects from the database and loading them into a couple of ListBoxes. That was working just fine, but now I need to override one of the properties of the Role objects I'm adding to the listboxes using this pseudocode process:
iterate over the List of Roles
look up a matching item out of a List of Profiles using List<T>.Find()
look up a property on the Profile
build up a new Role and set the Name property as needed
add the Role to a list of Roles for the PermissionsDialog
All of that goes smoothly, but when I call dialogPermissions.ShowDialog() the underlying framework code throws an AccessViolationException.
Here is what I believe to be the relevant code:
List<UserProfile> userProfiles = new List<UserProfile>();
List<Role> allRoles = new List<Role>();
dialogData.AllRoles = new List<Role>();
using (var objectContext = this.SiteContext.CreateObjectContext())
{
userProfiles = rs.UserProfiles.FindAll().ToList();
allRoles = rs.Roles.FindAll();
}
foreach (Role role in allRoles.Where(role => role.BuiltInId == (int)BuiltInRoleId.UserProfileRole)) {
var userProfile = userProfiles.Find(up => role.Id == up.Id);
var roleToAdd = new Role {
BuiltInId = role.BuiltInId,
Description = role.Description,
DirectoryEntry = role.DirectoryEntry,
Id = role.Id,
IsBuiltInRole = role.IsBuiltInRole,
Name = null != profile ? profile.DisplayName:role.Name
};
dialogData.AllRoles.Add(roleToAdd);
}
My suspicion is that this is somehow a deferred execution issue, but triggering execution by calling ToList() on dialogData.AllRoles before calling ShowDialog() doesn't resolve the issue. When I replace profile.DisplayName with a constant string I do not get the error.
Any clues what's going on under the covers here, or how to find out what's going on, or how to approach the problem differently so I can avoid it? All suggestions welcome ;-)
CONCLUSION
So here's the actual issue, I think:
Setting the Name property of the Role to null is just fine, but when the dialog tries to create a ListBoxItem out of the Role and uses the Role.Name property for the ListBoxItem's Content property (which is an Object), that can't be set as null and throws down in the framework code that's building up the dialog. Checking to make sure I had a value in there fixes the issue.
Seems like s strange exception to throw, but there you have it....
You test for profile != null, but don't test for profile.DisplayName != null so that's the first thing that comes to mind from just looking at your sample.
Standard exception finder is to go to Debug\Exceptions and check the box for break on thrown so you can see all the state when the exception is thrown.
You can stare at this code for a week and never find the reason for the AccessViolationException. Managed code does not die from processor faults like this one.
You'll need to dig out as much info you can get from the actual exception. That requires first of all that you enable unmanaged code debugging so that you can see the stack frames in the native code. Project + Properties, Debug tab, tick the "Enabled unmanaged code debugging" option.
Next, you want to make sure that you have .pdb file for any of the native Windows DLLs. Including the ones for Active Directory, somewhat suspect in this case. Tools + Options, Debugging, Symbols and enable the Microsoft Symbol Server. Press F1 if you have an older version of Visual Studio that doesn't make it a simple checkbox click.
Reproduce the crash, the call stack should give you a good hint what native code is suspect. Post it in your question if you can't make hay of it.