This document describes the PX1069 diagnostic.
| Code | Short Description | Type | Code Fix |
|---|---|---|---|
| PX1069 | The DAC must declare mandatory timestamp and audit DAC fields. | Error | Available |
Data Access Classes (DACs) in Acumatica Framework must declare mandatory system fields that are required for proper concurrent updates handling, data tracking and audit functionality. These mandatory fields include:
- tstamp: A timestamp field for optimistic concurrency control in the concurrent database update scenarios.
- CreatedByID: The ID of the user who created the record.
- CreatedByScreenID: The screen ID from which the record was created.
- CreatedDateTime: The date and time when the record was created.
- LastModifiedByID: The ID of the user who last modified the record.
- LastModifiedByScreenID: The screen ID from which the record was last modified.
- LastModifiedDateTime: The date and time when the record was last modified.
The PX1069 diagnostic determines whether any of these mandatory fields are missing from a DAC and reports an error. If multiple mandatory fields are missing, the diagnostic reports all missing fields in a single error message.
There are some exceptions to the PX069 diagnostic when the mandatory fields are not required, and thus the diagnostic will not be triggered. The diagnostic is not triggered in the following cases:
-
The DAC has the
PXAccumulatorattribute or an attribute derived from thePXAccumulatorattribute. Such DACs are used for special scenarios that involve accumulation of data. These scenarios are handled differently by the Acumatica Framework. Thus, DACs with thePXAccumulatorattribute do not require audit or timestamp fields. -
The DAC has the
PXProjectionattribute or an attribute derived from thePXProjectionattribute. These DACs (also called projection DACs) usually represent readonly database views or complex queries rather than direct table mappings. Such read-only projection DACs do not require audit or timestamp fields.However, Acumatica also supports writable projection DACs (also called persistent projection DACs). Such DACs also must declare audit and timestamp fields! Currently, the PX1069 diagnostic does not report the absence of these fields in projection DACs.
-
The DAC is fully unbound from the database, meaning there are no tables in the database that correspond to it. Such DACs are also known as virtual DACs. They do not require audit or timestamp fields because they do not interact with the database directly and do not persist any audit data.
Since Acuminator does not access the database, it determines whether a DAC is fully unbound based on the following rules:
- A DAC without any DAC fields or with only unbound DAC fields is considered fully unbound.
- A DAC marked with
PX.Data.PXVirtualAttributeis always considered fully unbound even if it has DB bound fields. - A DAC marked with
PX.Data.PXProjectionAttribute,PX.Data.PXAccumulatorAttributeor any other attribute derived fromPX.Data.PXDBInterceptorAttributeis always considered DB bound even if it has only unbound fields. - A DAC with a DB bound
NoteIDfield is considered fully unbound ifNoteIDis the only DB bound field in the DAC and all other fields in the DAC are unbound.
The PX1069 code fix automatically adds the missing mandatory fields to the DAC. The code fix intelligently inserts missing mandatory DAC fields according to the following rules:
- Audit fields are grouped together, with the Created audit fields placed before Last Modified audit fields. The fix respects the locations of existing audit fields and adds missing fields in the appropriate positions within the audit field group.
- The missing tstamp field is placed at the end of the DAC field declaration.
- The code fix preserves the
regiondirectives of the existing DAC fields. - Appropriate
usingdirectives (SystemandPX.Data.BQL) are automatically added if needed. - The code fix supports the context of nullable reference types and emits the correct nullable annotation for generated DAC fields if nullable reference types are enabled.
using System;
using PX.Data;
[PXCacheName("Incomplete DAC")]
public class IncompleteDac : PXBqlTable, IBqlTable
{
#region DacID
public abstract class dacID : PX.Data.BQL.BqlInt.Field<dacID> { }
[PXDBIdentity(IsKey = true)]
public virtual int? DacID { get; set; }
#endregion
#region Description
public abstract class description : PX.Data.BQL.BqlString.Field<description> { }
[PXDBString(255)]
public virtual string Description { get; set; }
#endregion
// Missing mandatory fields: tstamp, CreatedByID, CreatedByScreenID, CreatedDateTime,
// LastModifiedByID, LastModifiedByScreenID, LastModifiedDateTime
}using System;
using PX.Data;
using PX.Data.BQL;
[PXCacheName("Incomplete DAC")]
public class IncompleteDac : PXBqlTable, IBqlTable
{
#region DacID
[PXDBIdentity(IsKey = true)]
public virtual int? DacID { get; set; }
public abstract class dacID : PX.Data.BQL.BqlInt.Field<dacID> { }
#endregion
#region Description
[PXDBString(255)]
public virtual string Description { get; set; }
public abstract class description : PX.Data.BQL.BqlString.Field<description> { }
#endregion
#region CreatedByID
public abstract class createdByID : BqlGuid.Field<createdByID> { }
[PXDBCreatedByID]
public virtual Guid? CreatedByID { get; set; }
#endregion
#region CreatedByScreenID
public abstract class createdByScreenID : BqlString.Field<createdByScreenID> { }
[PXDBCreatedByScreenID]
public virtual string CreatedByScreenID { get; set; }
#endregion
#region CreatedDateTime
public abstract class createdDateTime : BqlDateTime.Field<createdDateTime> { }
[PXDBCreatedDateTime]
public virtual DateTime? CreatedDateTime { get; set; }
#endregion
#region LastModifiedByID
public abstract class lastModifiedByID : BqlGuid.Field<lastModifiedByID> { }
[PXDBLastModifiedByID]
public virtual Guid? LastModifiedByID { get; set; }
#endregion
#region LastModifiedByScreenID
public abstract class lastModifiedByScreenID : BqlString.Field<lastModifiedByScreenID> { }
[PXDBLastModifiedByScreenID]
public virtual string LastModifiedByScreenID { get; set; }
#endregion
#region LastModifiedDateTime
public abstract class lastModifiedDateTime : BqlDateTime.Field<lastModifiedDateTime> { }
[PXDBLastModifiedDateTime]
public virtual DateTime? LastModifiedDateTime { get; set; }
#endregion
#region tstamp
public abstract class Tstamp : BqlByteArray.Field<Tstamp> { }
[PXDBTimestamp]
public virtual byte[] tstamp { get; set; }
#endregion
}