We have a button "Start" that populates the dateStarted field to DateTime.Now..
When retrieving datetime, it always shows the server's date/time instead of user's local timezone version. How do i make it work like lastModifiedDateTime/CreatedDateTime that whenever we view it it's formatted as user's local timezone. I tried UseTimeZone = true/false but nothing is working
Here is my code that is not working
#region DateStarted
public abstract class dateStarted : PX.Data.IBqlField
{
}
protected DateTime? _DateStarted;
[PXDBDateAndTime(DisplayNameDate = "Date Started", DisplayNameTime = "Time", UseTimeZone = true)]
public virtual DateTime? DateStarted
{
get
{
return this._DateStarted;
}
set
{
this._DateStarted = value;
}
}
#endregion
It sounds like just using PX.Common.PXTimeZoneInfo.Now is enough to do the job.
PX.Common.PXTimeZoneInfo also has UtcNow, UtcToday, and Today if needed
Look up the code of class PXDBDateAndTime in Source Code screen SM204570. For debugging purpose you can copy the whole attribute and rename it to something else like PXDBDateAndTimeDebug:
[PXDBDateAndTimeDebug(DisplayNameDate = "Date Started", DisplayNameTime = "Time", UseTimeZone = true)]
With that approach you can debug SetUseTimeZone and GetTimeZone methods. The time zone used is coming from LocaleInfo.GetTimeZone method and you should debug that too:
public static PXTimeZoneInfo GetTimeZone()
{
if (!PXContext.PXIdentity.IsAnonymous() && PXContext.PXIdentity.TimeZone != null)
{
return PXContext.PXIdentity.TimeZone;
}
return PXTimeZoneInfo.Invariant;
}
The issue here is that from your question we can't tell if there's actually a problem with Acumatica time zone handling or if the user profile time zone is properly configured in your instance or if the result you're expecting is actually a valid ISO conversion as done by the DotNet framework. Debugging step by step should reveal what's going on.
After analyzing how CreatedDateTime and LastModifiedDatetime and other dates behave the same, the problem is in the input. So i created the following code to save the correct datetime with regards to the current user's timezone.
public static class DateTimeHelper
{
public static DateTime? Now()
{
var test = LocaleInfo.GetTimeZone();
PXTimeZoneInfo timezone = LocaleInfo.GetTimeZone();
DateTime dt = DateTime.UtcNow;
dt = PXTimeZoneInfo.ConvertTimeFromUtc(dt, timezone);
return dt;
}
}
and the implementation:
public PXAction<CQLMChecklists> startButton;
[PXUIField(DisplayName = "Start", Visible = true)]
[PXButton()]
public virtual void StartButton()
{
if (Document.Current != null)
{
CQLMChecklists doc = Document.Current;
Actions.PressSave();
CommenceChecklist(DateTimeHelper.Now(), DateTimeHelper.Now().Value, ref doc);
Document.Update(doc);
}
Actions.PressSave();
}
Related
I am building a simple time clock screen. Employee number is entered; using the FieldUpdated event the employee name is filled out, along with the current time (PX.Common.PXTimeZoneInfo.Now) in the Clock Time field. Clicking SAVE puts the data in the table and as you can see it is correct.
Also on the Acumatica screen UPTO this point, the time displayed is correct.
Upon refresh of the screen the time displayed is wrongly shown as 12:00.
My DAC code for the ClockTime looks like:
#region ClockTime
[PXDBDate()]
[PXUIField(DisplayName = "Clock Time")]
public virtual DateTime? ClockTime { get; set; }
public abstract class clockTime : PX.Data.BQL.BqlDateTime.Field<clockTime> { }
#endregion
And my event handler:
protected virtual void _(Events.FieldUpdated<EMPTimeEntries, EMPTimeEntries.employeeID> e)
{
EMPTimeEntries row = e.Row;
if (row.EmployeeID != null)
{
EPEmployee employeeCard = PXSelectorAttribute.Select<EMPTimeEntries.employeeID>(e.Cache, row) as EPEmployee;
row.EmployeeName = employeeCard.AcctName;
row.ClockTime = PX.Common.PXTimeZoneInfo.Now;
}
}
Any thoughts on why this is happening?
Let me know if you need any further information.
I think I have it. I added to my DAC attribute:
[PXDBDate(PreserveTime = true)]
Please advice if there is a more proper way, or if this is the way.
We have a customer who has requested to have the "Duration" or "End Time" field rounded up to the nearest quarter hour in Service Appointment entry (whichever makes the most sense) Service Appointment Entry screen
With the service appointments there's alot going on. Does anyone have any thoughts on where would be the best way to approach this? There are a lot of events going on that modify the duration and end time and I wouldn't want to have to modify all of them. I'm wondering if its possible to just modify something on the DAC to automatically round the end time up to the quarter hour.
Got Pretty far with modifying the DAC. No errors in the code and as I step through it I can see its rounding the time like I want it to. However the field isnt setting in the screen. Am i missing something silly?
namespace PX.Objects.FS
{
public class FSAppointmentDetServiceExt : PXCacheExtension<PX.Objects.FS.FSAppointmentDetService>
// public class FSAppointmentDetServiceExt : PXCacheExtension<PX.Objects.FS.FSAppointmentDetService>
{
#region ActualDateTimeEnd
[PXDBDateAndTime(UseTimeZone = true, PreserveTime = true, DisplayNameDate = "Actual Date End", DisplayNameTime = "Actual Time End - Nicole")]
[PXUIField(DisplayName = "Actual Date", Visibility = PXUIVisibility.SelectorVisible)]
public virtual DateTime? ActualDateTimeEnd
{
get
{
return this._ActualDateTimeEnd;
}
set
{
this._ActualDateTimeEnd = RoundUp(value, TimeSpan.FromMinutes(15));
}
}
#pragma warning disable PX1026 // Underscores cannot be used in DAC declarations
public DateTime? _ActualDateTimeEnd;
#pragma warning restore PX1026 // Underscores cannot be used in DAC declarations
public static DateTime? RoundUp(DateTime? dt, TimeSpan d)
{
DateTime nt = Convert.ToDateTime(dt);
if (nt != null)
{
return new DateTime((nt.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, nt.Kind);
}
else
{
return nt;
}
}
#endregion
}
}
I'm wondering if its possible to just modify something on the DAC to
automatically round the end time up to the quarter hour.
Yes this is possible and I would recommend it since it's the easiest (perhaps not best) approach.
Create a DAC extension in CODE section, override (replace) the field dates you need to round. Add a backing field to the property and modify the setter property to round the time.
Example:
[PXDBDateAndTime(UseTimeZone = true, PreserveTime = true, DisplayNameDate = "Actual Date Time Begin", DisplayNameTime = "Actual Start Time")]
[PXUIField(DisplayName = "Actual Date", Visibility = PXUIVisibility.SelectorVisible)]
public virtual DateTime? ActualDateTimeBegin
{
get { return _ActualDateTimeBegin; }
set
{
_ActualDateTimeBegin = RoundTime(value);
}
}
public DateTime? _ActualDateTimeBegin;
public DateTime? RoundTime(DateTime? dateTime)
{
// return the rounded datetime
}
While it is easy in thought to just limit records to INSite.branchID = AccessInfo.branchID, the need is a bit more complex. I thought that I found a simple solution when looking at the DAC for INTran to find:
#region SiteID
public abstract class siteID : PX.Data.BQL.BqlInt.Field<siteID> { }
protected Int32? _SiteID;
[IN.SiteAvail(typeof(INTran.inventoryID), typeof(INTran.subItemID))]
[PXDefault(typeof(INRegister.siteID))]
[PXForeignReference(typeof(FK.Site))]
[InterBranchRestrictor(typeof(Where<SameOrganizationBranch<INSite.branchID, Current<INRegister.branchID>>>))]
public virtual Int32? SiteID
{
get
{
return this._SiteID;
}
set
{
this._SiteID = value;
}
}
#endregion
which has an intriguing attribute for InterBranchRestrictor. After a little digging, I found this attribute actually is used rather widely in Acumatica, but it appears to be limited to only Report Graphs and enabling the feature for Inter-Branch Transactions. Easy enough, I enabled the feature and tried an Inventory Issue again. No luck. I still could select a site id for a different branch.
So far, I only have limited control by creating a graph extension on INIssueEntry to set and validate the site ID. But what I really want is to limit the selector to only site id's of the current branch.
protected void _(Events.FieldDefaulting<INTran.siteID> e)
{
PXResultset<INSite> Results = PXSelect<INSite, Where<INSite.branchID, Equal<Current<AccessInfo.branchID>>>>.Select(Base);
if (Results.Count == 1)
{
foreach (PXResult<INSite> result in Results)
{
INSite site = result;
e.NewValue = site.SiteID;
e.Cancel = true;
}
}
}
protected void _(Events.FieldVerifying<INTran.siteID> e)
{
int? siteID = (int?)e.NewValue;
INTran row = (INTran)e.Row;
INSite site = PXSelect<INSite, Where<INSite.siteID, Equal<Required<INSite.siteID>>,
And<INSite.branchID, Equal<Current<AccessInfo.branchID>>>>>.Select(Base, siteID);
if(siteID != null && site?.SiteID == null)
{
PXUIFieldAttribute.SetError<INTran.siteID>(e.Cache, row, "Invalid Warehouse for Branch");
}
}
I really want to leverage what it seems like
[InterBranchRestrictor(typeof(Where<SameOrganizationBranch<INSite.branchID, Current<INRegister.branchID>>>))]
does, but clearly I just don't understand what it does. Alternatively, if I could add a where clause to
[IN.SiteAvail(typeof(INTran.inventoryID), typeof(INTran.subItemID))]
then I could restrict the list to the current branch that way, but I'm struggling to make that work as well. (Problems around implementing the PXForeignReference attribute in the extension needed to override the field definition.)
How can I restrict (in a manner that can be replicated efficiently throughout Acumatica) branch specific records to only site ID's of the current branch?
The InterBranchRestrictorAttribute is checking the graph to be working from a Report or the Inter-Branch Transactions feature to be turned on in the IsReportOrInterBranchFeatureEnabled method, so you need to remove this from your implementation.
You can write your own PXResrictorAttribute in the way shown in the example below:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public class CustomRestrictorAttribute: PXRestrictorAttribute
{
public CustomRestrictorAttribute(Type where) : base(CustomRestrictorAttribute.EmptyWhere, "Restrictor Message.", Array.Empty<Type>())
{
this._interBranchWhere = where;
}
protected override BqlCommand WhereAnd(PXCache sender, PXSelectorAttribute selattr, Type Where)
{
return base.WhereAnd(sender, selattr, this._interBranchWhere);
}
private static readonly Type EmptyWhere = typeof(Where<True, Equal<True>>);
protected Type _interBranchWhere;
}
And apply it to the DAC field like below:
[SiteAvail(typeof(SOLine.inventoryID), typeof(SOLine.subItemID))]
[PXParent(typeof(Select<SOOrderSite, Where<SOOrderSite.orderType, Equal<Current<SOLine.orderType>>, And<SOOrderSite.orderNbr, Equal<Current<SOLine.orderNbr>>, And<SOOrderSite.siteID, Equal<Current2<SOLine.siteID>>>>>>), LeaveChildren = true, ParentCreate = true)]
[PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIRequired(typeof(IIf<Where<SOLine.lineType, NotEqual<SOLineType.miscCharge>>, True, False>))]
[InterBranchRestrictor(typeof(Where2<SameOrganizationBranch<INSite.branchID, Current<SOOrder.branchID>>,
Or<Current<SOOrder.behavior>, Equal<SOBehavior.qT>>>))]
[CustomInterBranchRestrictor(typeof(Where<INSite.branchID,Equal<Current<SOOrder.branchID>>>))]
protected virtual void SOLine_SiteID_CacheAttached(PXCache cache)
{
}
I have a bindingsource control called binding, on a form in VS2012, and a DateTimePicker control bound to it.
for the binding properties I have MinDate = 1/01/1753 and MaxDate = 31/12/9998
Value has been set by picking Today from the calender 5/04/2013 11:27 AM
I set a bindingsource up using
var dset = base.Context.ContactEvents;
var qry = dset.Where(p => p.Id > 0).OrderBy(x => x.Id);
qry.Load();
this.bindingSource.DataSource = dset.Local.ToBindingList();
The bindingsource is used in the following manner;
public void RefreshBindingDataSourceAndPosition(BindingSource binding)
{
binding.DataSource = this.bindingSource.DataSource; // error raised here
binding.Position = this.bindingSource.Position;
}
The error information is
System.ArgumentOutOfRangeException crossed a native/managed boundary
HResult=-2146233086
Message=Value of '1/01/0001 12:00:00 AM' is not valid for 'Value'. 'Value' should be between 'MinDate' and 'MaxDate'.
Parameter name: Value
Source=System.Windows.Forms
ParamName=Value
StackTrace:
at System.Windows.Forms.DateTimePicker.set_Value(DateTime value)
InnerException:
I can work around the problem by not binding the Data Picker , and setting it in the EventsBindingSource_CurrentChanged event
However it seems odd to have to do this. How can I get the databinding working?
[Update]
This problem is similar to the one described here
I have tried to reproduce the problem in a simpler project so as to try and isolate the cause, however it works in the simpler project. Also the project works on another computer.
The problem occurs on my computer with both SQL Server 2012 and 2008R2. I have tried altering the date format and country in control panel. Also I have tried different settings for the format property. I have also tried setting the date field to support null.
When I copy the error to the clipboard it shows the following ;
System.Reflection.TargetInvocationException occurred
HResult=-2146232828
Message=Exception has been thrown by the target of an invocation.
Source=mscorlib
StackTrace:
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
InnerException: System.ArgumentOutOfRangeException
HResult=-2146233086
Message=Value of '1/01/0001 12:00:00 AM' is not valid for 'Value'. 'Value' should be between 'MinDate' and 'MaxDate'.
Parameter name: Value
Source=System.Windows.Forms
ParamName=Value
StackTrace:
at System.Windows.Forms.DateTimePicker.set_Value(DateTime value)
InnerException:
My EF class is as follows
public class ContactEvent : LoggedEntity
{
public virtual SortableBindingList<ContactEventAttendee> Attendees { get; private set; }
public virtual ContactEventType ContactEventType { get; set; }
public string Details { get; set; }
public DateTime? EventTime { get; set; }
public virtual SortableBindingList<ContactEventItem> Items { get; private set; }
public int State { get; set; }
public string Title { get; set; }
public override string ToString()
{
return "Contact Events";
}
}
it inherits from
public abstract class LoggedEntity
{
public LoggedEntity()
{
this.RowId = Guid.NewGuid();
this.RowVersionId = 0;
AppDomain dm = AppDomain.CurrentDomain; // Gets the current application domain for the current Thread.
object s = AppDomain.CurrentDomain.GetData("SiteNumber");
this.SourceSiteNumber = Convert.ToInt32(s);
}
public LoggedEntity(int SiteNumber)
{
// the following 3 are used to identify the version
this.RowId = Guid.NewGuid();
this.RowVersionId = 0;
this.SourceSiteNumber = SiteNumber;
}
public int Id { get; set; }
public Guid RowId { get; set; }
[ConcurrencyCheck]
public int RowVersionId { get; set; }
public int SourceSiteNumber { get; set; }
}
[update]
A similar problem is here
[update]
Another here makes me think I need to look at how keys are being processed.
[update]
I noticed the following in the output window
A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in System.Windows.Forms.dll
[update]
This led me to
here
and after turning on the debug options I found an error
Invalid object name 'dbo.__MigrationHistory'.
however that is a known bug in EF5
[Update]: I found another person with similar unsolved issues here
Discovered I dont have problems when running the .EXE
[update] I can skip over the error by disabling "Break when exceptions cross App Domain or managed/native boundary
in Tools->Options->Debugging->General
[update] I adding the following, so I could inspect the control properties.
private void EventsBindingSource_BindingComplete(object sender, BindingCompleteEventArgs e)
{
// If the BindingComplete state is anything other than success,
// set the ErrorProvider to the error message.
if (e.BindingCompleteState != BindingCompleteState.Success)
{
errorProvider1.SetError((Control)e.Binding.BindableComponent, e.ErrorText);
var errdesc = e.ErrorText;
var ctrl = (Control)e.Binding.BindableComponent;
var info = string.Format(
"{0} {1}",errdesc,
ctrl.ToString());
Debug.Print(info);
// "Value of '1/1/0001 12:00:00 AM' is not valid for 'Value'.
'Value' should be between 'MinDate' and 'MaxDate'.\r\nParameter name:
Value System.Windows.Forms.DateTimePicker, Value: 1/1/1900 12:00:00 AM"
}
else
{
errorProvider1.SetError((Control)e.Binding.BindableComponent, "");
}
}
The cause of the exception may be that the DatePicker's DataBinding "Value" property has been set to a BindingSource field.
Only the DatePicker's DataBinding "Text" property needs to be set for the data binding to work correctly.
Check if there is a value in the DatePicker's DataBinding "Value" property field, once removed the issue should go away.
It seems that the key problem is the property is a Nullable DateTime. Probably the null value used to transcribe the DateTimePicker component is '1 / 01/0001 12:00:00 AM 'which creates a problem related to the configuration of MinValue and MaxValue. Using the Advanced tab of the DataBinding has an option to set the value to be used to null One way to solve this would be setting the value to null as MinDate, or we can set the MinDate with the value '01 / 01/0001 12:00 : 00 AM '. Despite my limited experience, I believe that this may be the source of your problems. the link http://msdn.microsoft.com/en-us/library/aa480734.aspx can see something else on the Advanced tab of the property DataBinding.
I added a new column(DateCreated) in a table(Activity) in sqlserver.
and I am using subsonic 2.0, how can I add this column as property in subsonic partial class, so that I can insert and update the value of "DateCreated" Column.
DateCreated will be provided by the user from the GUI.
following is the code I m using but it insert NULL & retrive NULL from the database.
public partial class ActivityInscription
{
public struct Columns
{
public static string IsInMixedList = #"IsInMixedList";
}
public bool? IsInMixedList
{
get;
set;
}
}
Please any one help me to resolve this issue.
If you added the column to the database then just rebuild the DAL. It will pick up the new column and add it to the subsonic DAL. There is no reason for a Partial class in your case. This will work providing that the table "Activity" is part of your subsonic list (includeTableList) of tables in the config file.
Following is the solution, I used and it is working:
public partial class Activity
{
public DateTime? DateCreated
{
get;
set;
}
protected override void BeforeInsert()
{
TableSchema.Table tblSchema = Schema;
TableSchema.TableColumn ccDateCreated = new TableSchema.TableColumn(tblSchema);
ccrDateCreated.ColumnName = "DateCreated";
ccDateCreated.DataType = DbType.DateTime;
ccDateCreated.MaxLength = 0;
ccDateCreated.AutoIncrement = false;
ccDateCreated.IsNullable = false;
ccDateCreated.IsPrimaryKey = false;
ccDateCreated.IsForeignKey = false;
ccDateCreated.IsReadOnly = false;
ccDateCreated.DefaultSetting = #"";
ccDateCreated.ForeignKeyTableName = "";
if (!tblSchema.Columns.Contains("DateCreated"))
{
tblSchema.Columns.Add(ccDateCreated);
}
if (this.GetSchema().Columns.Contains("DateCreated"))
this.SetColumnValue(Columns.DateCreated, DateCreated);
base.BeforeInsert();
}
}
Now it is working fine & inserting the values that I provide from the GUI.