I'm trying to extend the calculation of terms for invoices (Field TermsID, on Bills and Ajustments screen (AP301000)
The standard behaviour of Acumatica is : When you update the termsID field, it automatically updates the dueDate field and the discountDate field
So my idea was to look at the code in APInvoiceEntry and look for the TermsID_FieldUpdated
protected virtual void APInvoice_TermsID_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
Terms terms = (Terms)PXSelectorAttribute.Select<APInvoice.termsID>(sender, e.Row);
if (terms != null && terms.InstallmentType != TermsInstallmentType.Single)
{
foreach (APAdjust adj in Adjustments.Select())
{
Adjustments.Cache.Delete(adj);
}
}
}
But it doesnt seem to do anything except delete the cache of the adjustments. And I dont know what to extend in order to add another way of calculating the terms dates.
(The goal is to allow the following calculation :
- I take the invoice date, add 30 days, if i'm below the 10th day of the next month, i set the due date to the 10th day of the next month, else I set the due date to the 10th day of the next next month).
Thanks,
I added a FieldUpdated event handler on DueDate and examined the call stack to find the method which modify DueDate:
It's the [Terms(...)] attribute that contains the logic:
#region TermsID
public abstract class termsID : IBqlField
{
}
/// <summary>
/// The <see cref="PX.Objects.CS.Terms">credit terms</see> associated with the document (unavailable for prepayments and debit adjustments).\
/// Defaults to the <see cref="Vendor.TermsID">credit terms of the vendor</see>.
/// </summary>
[PXDBString(10, IsUnicode = true)]
[PXDefault(typeof(Search<Vendor.termsID,
Where<Vendor.bAccountID, Equal<Current<APInvoice.vendorID>>,
And<Current<APInvoice.docType>, NotEqual<APDocType.debitAdj>>>>),
PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Terms", Visibility = PXUIVisibility.Visible)]
[APTermsSelector]
[Terms(typeof(APInvoice.docDate), typeof(APInvoice.dueDate), typeof(APInvoice.discDate), typeof(APInvoice.curyOrigDocAmt), typeof(APInvoice.curyOrigDiscAmt))]
public virtual string TermsID
{
get;
set;
}
#endregion
Inside the Terms attribute you'll find the code modifying due date.
You could look into providing a custom attribute or bypassing that logic.
public static void CalcTermsDates(Terms terms, DateTime? docDate, out DateTime? dueDate, out DateTime? discDate)
{
dueDate = null;
discDate = null;
if (docDate != null && terms != null)
{
DateTime DocDate = docDate.Value;
switch (terms.DueType)
{
case TermsDueType.FixedNumberOfDays:
dueDate = DocDate.AddDays((double)terms.DayDue00);
break;
case TermsDueType.Prox:
DateTime sameDayOfNextMonth = DocDate.AddMonths(1);
DateTime firstDayOfNextMonth = new DateTime(sameDayOfNextMonth.Year, sameDayOfNextMonth.Month, 1);
dueDate = firstDayOfNextMonth.AddDays((double)terms.DayDue00);
break;
case TermsDueType.DayOfNextMonth:
dueDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDue00).AddMonths(1);
break;
case TermsDueType.DayOfTheMonth:
int monthShift = DocDate.Day > (int)terms.DayDue00 ? 1 : 0;
dueDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDue00).AddMonths(monthShift);
break;
case TermsDueType.Custom:
int nextmonth = 0;
if (DocDate.Day >= terms.DayFrom00 && DocDate.Day <= terms.DayTo00)
{
if (terms.DayDue00 <= terms.DayTo00)
{
nextmonth = 1;
}
dueDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDue00).AddMonths(nextmonth);
}
if (DocDate.Day >= terms.DayFrom01 && DocDate.Day <= terms.DayTo01)
{
if (terms.DayDue01 <= terms.DayTo01)
{
nextmonth = 1;
}
dueDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDue01).AddMonths(nextmonth);
}
break;
case TermsDueType.EndOfMonth:
dueDate = new DateTime(DocDate.Year, DocDate.Month, 1).AddMonths(1).AddDays(-1);
break;
case TermsDueType.EndOfNextMonth:
dueDate = new DateTime(DocDate.Year, DocDate.Month, 1).AddMonths(2).AddDays(-1);
break;
default:
break;
}
if (terms.InstallmentType == TermsInstallmentType.Multiple)
{
discDate = dueDate;
}
else
{
switch (terms.DiscType)
{
case TermsDueType.FixedNumberOfDays:
discDate = DocDate.AddDays((double)terms.DayDisc);
break;
case TermsDueType.Prox:
DateTime sameDayOfNextMonth = DocDate.AddMonths(1);
DateTime firstDayOfNextMonth = new DateTime(sameDayOfNextMonth.Year, sameDayOfNextMonth.Month, 1);
discDate = firstDayOfNextMonth.AddDays((double)terms.DayDisc);
break;
case TermsDueType.DayOfNextMonth:
discDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDisc).AddMonths(1);
break;
case TermsDueType.DayOfTheMonth:
int monthShift;
if (terms.DueType == TermsDueType.DayOfNextMonth && DocDate.Day <= (int)terms.DayDue00)
monthShift = DocDate.Day >= (int)terms.DayDisc ? 1 : 0;
else if (terms.DueType == TermsDueType.EndOfNextMonth)
monthShift = DocDate.Day >= (int)terms.DayDisc ? 1 : 0;
else
monthShift = DocDate.Day > (int)terms.DayDue00 ? 1 : 0;
discDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDisc).AddMonths(monthShift);
break;
case TermsDueType.EndOfMonth:
discDate = new DateTime(DocDate.Year, DocDate.Month, 1).AddMonths(1).AddDays(-1);
break;
case TermsDueType.EndOfNextMonth:
discDate = new DateTime(DocDate.Year, DocDate.Month, 1).AddMonths(2).AddDays(-1);
break;
default:
break;
}
}
if (discDate > dueDate)
{
discDate = dueDate;
}
}
}
You can create a new attribute (ex: MyTerms) deriving from an existing one (ex: Terms) and override methods (ex: CalcTerms). You then redefine/extend the field that is using that attribute (TermsID) so it uses your attribute (MyTerms) instead of (Terms).
The major issue is when the code you want to change is in methods that can't be overridden, in that case you need to copy paste the code wholesale in your custom attribute and forgo inheritance.
The starting point for that is the Attribute Code. To get it, use VIEW SOURCE feature in customization project editor:
Then use 'Find in files' to get the attribute code:
Implement a APInvoice_DueDate_FieldUpdated event handler, it will be raised when the term is changed.
Related
I 've continously getting error:
2019-05-06 14:37:40,128 [EPI-TaskExecutor-1] ERROR
ScriptExecutionExceptionHandler.handleScriptError(31) - Script with id
[3e2fd082-62f3-46a2-8fca-71b1f9cef028] and label [Outage definition
script] reports an error: : No such property: getToHour for class:
OutagePolicyScript.
Outages.xml file is OK, and is placed together with outage.dtd file in correct location.
import java.util.List;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import com.hp.opr.api.scripting.Event;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.lang3.ArrayUtils;
class OutagePolicyScript
{
Object syncObject = new Object();
final static String fileName = "/opt/HP/BSM/Temp/outages.xml"; // for Windows it will be: "d:/HPBSM/Temp/outages.xml";
final static Log s_log = LogFactory.getLog("com.hp.opr.epi.OutagePolicyScript");
List<OutageDefinition> outageDefinitions = new ArrayList<OutageDefinition>();
Thread loadXMLThread;
final boolean isSuppressEventsOrClose = true;
def init()
{
LoadOutagesFromXML r1 = new LoadOutagesFromXML();
loadXMLThread = new Thread(r1);
loadXMLThread.start();
s_log.debug("init finished");
}
def destroy()
{
s_log.debug("going to stop thread....");
loadXMLThread.interrupt();
}
def process(List<Event> events)
{
synchronized(syncObject) {
try
{
events.each {Event event ->
handleEventSuppressionIfNeeded(event, "EST");
//event.setTitle("Modified by CA/EPI: " + event.getTitle());
// always include following check for each event
if(Thread.interrupted())
throw new InterruptedException()
}
}
catch(InterruptedException e)
{
s_log.error("process of events interrupted", e);
}
}
}
private void handleEventSuppressionIfNeeded(Event event, String timezoneId)
{
// Calculate if event was received during the accepted time window.
Date timeRecieved = event.getTimeReceived();
Calendar cal = Calendar.getInstance();
TimeZone tz = TimeZone.getTimeZone(timezoneId);
if (tz != null)
cal = Calendar.getInstance(tz);
cal.setTime(timeRecieved);
int hour = cal.get(Calendar.HOUR_OF_DAY);
int minute = cal.get(Calendar.MINUTE);
int dow = cal.get(Calendar.DAY_OF_WEEK);
//event.addCustomAttribute("hour", Integer.toString(hour));
//event.addCustomAttribute("minute", Integer.toString(minute));
//event.addCustomAttribute("day of week", Integer.toString(dow));
// go over all the outage definitions, and compare event attributes to the rule
outageDefinitions.each {OutageDefinition outage ->
if (outage.getIsActive()) {
s_log.debug("Checking active rule: " + outage.getDescription());
// check if the event's day of week is one of the outage definition days. in case the rule cross day, reduce 1 for day of week
if (ArrayUtils.contains(outage.getDays(), dow) || (outage.getCrossDay() && ArrayUtils.contains(outage.getDays(), (dow == 1) ? 7 : dow-1 ))) {
// check if event hour and minute are inside rule's from/to
// convert all configurations to minutes
// if the rule cross a day, then add to "to" 24 hours in order to compare from <= event_time < to
// if the rule cross a day AND event hour < "from" then it means the event is in the next day and need to add 24 hours too
int eventTimeInMin = ((hour < outage.getFromHour() && outage.getCrossDay()) ? hour + 24 : hour) * 60 + minute;
int fromInMin = outage.getFromHour() * 60 + outage.getFromMinute();
int toInMin = (outage.getCrossDay() ? outage.getToHour() + 24 : outage.getToHour) * 60 + outage.getToMinute();
if (eventTimeInMin >= fromInMin && eventTimeInMin < toInMin) {
s_log.debug("event time is within this outage rule, proceed to compare event's attributes");
boolean foundMatch = true;
Set<String> attributeNames = outage.getAttributesList().keySet();
attributeNames.each {String name ->
boolean attMatchResult;
// at the moment, all comparisons are using "EQ"
switch (name) {
case "title" : attMatchResult = compareEventAttributeToOutageRuleForcontains("title", event.getTitle(), outage.getAttributesList().get(name)); break;
case "application" : attMatchResult = compareEventAttributeToOutageRule("application", event.getApplication(), outage.getAttributesList().get(name)); break;
case "object" : attMatchResult = compareEventAttributeToOutageRule("object", event.getObject(), outage.getAttributesList().get(name)); break;
case "category" : attMatchResult = compareEventAttributeToOutageRule("category", event.getCategory(), outage.getAttributesList().get(name)); break;
case "subcategory" : attMatchResult = compareEventAttributeToOutageRule("subcategory", event.getSubCategory(), outage.getAttributesList().get(name)); break;
case "nodeHint" : attMatchResult = compareEventAttributeToOutageRuleForcontains("nodeHint", event.getNodeHints().getHint(), outage.getAttributesList().get(name)); break;
default : s_log.error("attribute name [" + name + "] from outages.xml is not supported");
}
s_log.debug("result for attribute [" + name + "] is: " + attMatchResult);
foundMatch &= attMatchResult;
}
s_log.debug("result after processing all attributes in the rule is: " + foundMatch);
if (foundMatch) {
if (isSuppressEventsOrClose)
event.addCustomAttribute("SuppressDueToOutageRule", outage.getDescription());
else
event.setState(LifecycleState.CLOSED);
}
}
else {
s_log.debug("Current rule doesnt match for the event's time, this rule will be skipped");
}
}
else {
s_log.debug("Current rule doesnt match for the event's day, this rule will be skipped");
}
}
}
}
private boolean compareEventAttributeToOutageRule(String eventAttName, String eventAttValue, Set<String> ruleValues)
{
if (ruleValues.contains(eventAttValue)) {
s_log.debug("found match on attribute [" + eventAttName + "], attribute value=" + eventAttValue);
return true;
}
// else, no match
s_log.debug("no match for attribute " + eventAttName);
return false;
}
private boolean compareEventAttributeToOutageRuleForcontains(String eventAttName, String eventAttValue, Set<String> ruleValues)
{
Iterator<String> itr = ruleValues.iterator();
while (itr.hasNext()) {
if (eventAttValue.indexOf(itr.next()) != -1) { // check if the event attribute contains the rule's value
s_log.debug("found match on attribute [" + eventAttName + "], attribute value=" + eventAttValue);
return true;
}
}
// else, no match
s_log.debug("no match for attribute " + eventAttName);
return false;
}
class LoadOutagesFromXML implements Runnable
{
long lastModifiedTime = -1;
public void run()
{
while (!Thread.interrupted()) {
s_log.debug("in running.... current time: " + new Date());
// lock the sync object
synchronized(syncObject) {
long before = System.currentTimeMillis();
readOutageFile();
long after = System.currentTimeMillis();
s_log.debug("Loading outages definitions from XML took [" + (after - before) + "] ms.");
}
Thread.sleep(60000); // 60 seconds
}
s_log.debug("thread interrupted....");
}
private void readOutageFile()
{
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
File f = new File(fileName);
if (f.lastModified() == lastModifiedTime) {
// the file wasnt changed, no need to reload file
s_log.debug("file LastModifiedTime didn't change, no need to reload configuration");
return;
}
// otherwise, clear previous results and reload XML file
outageDefinitions.clear();
s_log.debug("going to load outage.xml");
Document doc = db.parse(f);
NodeList outages = doc.getElementsByTagName("outage");
for (int i=0 ; i<outages.getLength() ; ++i) {
s_log.debug("handle outage: " + Integer.toString(i));
// going to handle specific outage definition
Element aOutage = (Element)outages.item(i);
Element aTimeWindow = (Element)(aOutage.getElementsByTagName("timeWindow").item(0));
outageDefinitions.add(new OutageDefinition(aOutage.getAttribute("description"),
aOutage.getAttribute("isEnabled"),
aTimeWindow.getElementsByTagName("days").item(0).getTextContent(),
parseIntFromStr(aTimeWindow.getElementsByTagName("from").item(0).getTextContent(), true),
parseIntFromStr(aTimeWindow.getElementsByTagName("from").item(0).getTextContent(), false),
parseIntFromStr(aTimeWindow.getElementsByTagName("to").item(0).getTextContent(), true),
parseIntFromStr(aTimeWindow.getElementsByTagName("to").item(0).getTextContent(), false),
parseAttributesFromXML((Element)aOutage.getElementsByTagName("eventsFilter").item(0))));
}
lastModifiedTime = f.lastModified();
s_log.debug("Going to keep lastModifiedTime as " + lastModifiedTime);
}
catch (Exception e) {
s_log.error("caught exception during parsing of outage.xml", e);
lastModifiedTime = -1;
}
}
private int parseIntFromStr(String str, boolean isHour)
{
String[] array = str.trim().split(":");
if (array.length != 2) {
s_log.debug("Bad time format, expect HH:MM format but recieved: " + str);
return -1;
}
if (isHour)
return Integer.parseInt(array[0]);
else
return Integer.parseInt(array[1]);
}
private Map<String, Set<String>> parseAttributesFromXML(Element eventsFilter)
{
Map<String, Set<String>> result = new HashMap<String, Set<String>>();
NodeList eventAttributes = eventsFilter.getElementsByTagName("eventAttribute");
for (int i=0 ; i<eventAttributes.getLength() ; ++i) {
NodeList attributeValues = eventAttributes.item(i).getElementsByTagName("value");
Set<String> values = new HashSet<String>(attributeValues.getLength());
for (int j=0 ; j<attributeValues.getLength() ; j++) {
values.add(attributeValues.item(j).getTextContent());
}
result.put(eventAttributes.item(i).getAttribute("name"), values);
}
return result;
}
}
class OutageDefinition
{
String description;
boolean isActive;
int[] days;
int fromHour;
int fromMinute;
int toHour;
int toMinute;
boolean crossDay;
Map<String, Set<String>> attributesList;
public OutageDefinition(String desc, String active, String outageDays, int oFromHour, int oFromMinute, int oToHour, int oToMinute, Map<String, Set<String>> oAttributes) {
this.description = desc;
this.isActive = Boolean.parseBoolean(active);
this.fromHour = oFromHour;
this.fromMinute = oFromMinute;
this.toHour = oToHour;
this.toMinute = oToMinute;
this.attributesList = oAttributes;
this.crossDay = false;
// check if time cross a day
if (this.fromHour > this.toHour) {
s_log.debug("in rule [" + this.description + "] found time condition that crosses midnight, adjust toHour accordingly");
this.crossDay = true;
}
this.days = getDaysFromString(outageDays);
}
private int[] getDaysFromString(String str)
{
String[] days = str.trim().split(",");
int[] result = new int[days.length];
for (int i=0 ; i<days.length ; ++i) {
switch (days[i]) {
case "Sunday" : result[i] = 1; break;
case "Monday" : result[i] = 2; break;
case "Tuesday" : result[i] = 3; break;
case "Wednesday" : result[i] = 4; break;
case "Thursday" : result[i] = 5; break;
case "Friday" : result[i] = 6; break;
case "Saturday" : result[i] = 7; break;
default : result[i] = -1; break;
}
}
return result;
}
public String getDescription() {
return description;
}
public boolean getIsActive() {
return isActive;
}
public int[] getDays() {
return days;
}
public int getFromHour() {
return fromHour;
}
public int getFromMinute() {
return fromMinute;
}
public int getToHour() {
return toHour;
}
public int getToMinute() {
return toMinute;
}
public Map<String, Set<String>> getAttributesList() {
return attributesList;
}
public boolean getCrossDay() {
return crossDay;
}
}
}
Look closely at this line. You must either call a method (outage.getToHour()) or access a property (outage.getToHour), but here you do both things. Choose what is correct
int toInMin = (outage.getCrossDay() ? outage.getToHour() + 24 : outage.getToHour) * 60 + outage.getToMinute();
I need advice either to convert the SQL to BQL or set the Resultset manipulated by C# code to the PXSelector.
I need to customize the AR Invoice and add 2 custom fields to record the COGS GL account and sub account for the inter company client when the inter company enter this invoice line as a bill. This custom field need to look up all sub accounts that is restricted to this Client's Branch ID and GL account. Basically all system's sub account lookup take care of the restriction group but for the custom fields; a custom PXSelector need to be written for this. Below is the SQL that supplies the require sub accounts but I need to know how to make the SQL query works in Acumatica
-- SQL for the required data
DECLARE #GLAccountCD nvarchar(10) = 'COGS'
DECLARE #BranchCD nvarchar(30) = 'PurchaseBranch'
SELECT *
FROM Sub
where (((CAST(Sub.groupmask as int) & CAST((SELECT GroupMask FROM Account WHERE AccountCD = #GLAccountCD AND CompanyID = 3 AND DeletedDatabaseRecord = 0) AS int)) > 1
AND (CAST(Sub.groupmask as int) & CAST((SELECT GroupMask FROM Branch WHERE BranchCD = #BranchCD AND CompanyID = 3 AND DeletedDatabaseRecord = 0) AS int)) > 1)
OR (Sub.GroupMask = 0 AND Sub.DeletedDatabaseRecord = 0))
AND CompanyID = 3
ORDER BY SubCD
--The below PXSelector provide all sub accounts regard of restriction group,
--I need the PXSelector to use the above SQL Query result
#region UsrAPBIllGLSubAccID
[PXDBInt]
[PXUIField(DisplayName="Bill COGS SubAccount")]
[PXSelector(typeof(Search<Sub.subID, Where<Sub.active, Equal<True>>, OrderBy<Desc<Sub.subCD>>>),
new Type[] {typeof(Sub.subCD),
typeof(Sub.description)},
SubstituteKey = typeof(Sub.subCD)
)]
public virtual int? UsrAPBIllGLSubAccID { get; set; }
public abstract class usrAPBIllGLSubAccID : IBqlField { }
#endregion
I think that would be achievable using the Match BQL clause.
GLAccess.cs file has BQL queries to restrict accounts based on Sub and Branch group mask using Match clause, this would be a good place to investigate:
public PXSelect<Sub> Sub;
protected virtual IEnumerable sub(
)
{
if (Group.Current != null && !String.IsNullOrEmpty(Group.Current.GroupName))
{
bool inserted = (Group.Cache.GetStatus(Group.Current) == PXEntryStatus.Inserted);
foreach (Sub item in PXSelect<Sub,
Where2<Match<Current<PX.SM.RelationGroup.groupName>>,
Or2<Match<Required<Sub.groupMask>>, Or<Sub.groupMask, IsNull>>>>
.Select(this, new byte[0]))
{
if (!inserted || item.Included == true)
{
Sub.Current = item;
yield return item;
}
else if (item.GroupMask != null)
{
PX.SM.RelationGroup group = Group.Current;
bool anyGroup = false;
for (int i = 0; i < item.GroupMask.Length && i < group.GroupMask.Length; i++)
{
if (group.GroupMask[i] != 0x00 && (item.GroupMask[i] & group.GroupMask[i]) == group.GroupMask[i])
{
Sub.Current = item;
yield return item;
}
anyGroup |= item.GroupMask[i] != 0x00;
}
if (!anyGroup)
{
Sub.Current = item;
yield return item;
}
}
}
}
else
{
yield break;
}
}
public PXSelect<Branch> Branch;
protected virtual IEnumerable branch(
)
{
if (Group.Current != null && !String.IsNullOrEmpty(Group.Current.GroupName))
{
bool inserted = (Group.Cache.GetStatus(Group.Current) == PXEntryStatus.Inserted);
foreach (Branch item in PXSelect<Branch,
Where2<Match<Current<PX.SM.RelationGroup.groupName>>,
Or<Match<Required<Branch.groupMask>>>>>
.Select(this, new byte[0]))
{
if (!inserted)
{
Branch.Current = item;
yield return item;
}
else if (item.GroupMask != null)
{
PX.SM.RelationGroup group = Group.Current;
bool anyGroup = false;
for (int i = 0; i < item.GroupMask.Length && i < group.GroupMask.Length; i++)
{
if (group.GroupMask[i] != 0x00 && (item.GroupMask[i] & group.GroupMask[i]) == group.GroupMask[i])
{
Branch.Current = item;
yield return item;
}
anyGroup |= item.GroupMask[i] != 0x00;
}
if (!anyGroup)
{
Branch.Current = item;
yield return item;
}
}
}
}
else
{
yield break;
}
}
I have an action where a new customer location is created.
So far, I'm just trying to load the page with an existing record.
With this code, the result is positive
public virtual IEnumerable NewLocation(PXAdapter adapter)
{
CustomerLocationMaint locationGraph = PXGraph.CreateInstance<CustomerLocationMaint>();
Location locationRow = new Location();
locationGraph.Location.Current = locationGraph.Location.Search<Location.locationID>(116, "ABARTENDE");
locationGraph.Location.Insert(locationRow);
throw new PXRedirectRequiredException(locationGraph, null) { Mode = PXBaseRedirectException.WindowMode.NewWindow };
return adapter.Get();
}
However, this other version loads the page blank:
public virtual IEnumerable NewLocation(PXAdapter adapter)
{
CustomerLocationMaint locationGraph = PXGraph.CreateInstance<CustomerLocationMaint>();
Location locationRow = new Location();
locationRow.BAccountID = 109; //ABARTENDE
locationRow.LocationID = 116; //MAIN
locationGraph.Location.Insert(locationRow);
throw new PXRedirectRequiredException(locationGraph, null) { Mode = PXBaseRedirectException.WindowMode.NewWindow };
return adapter.Get();
}
I need to have a version similar to the second one, because eventually, the new LocationCD will be entered from this action.
Any ideas?
In the second example, you're trying to set the LocationID explicitly, this is an identity field that needs to be assigned. I found a couple of examples in the source:
public PXDBAction<BAccount> addLocation;
[PXUIField(DisplayName = Messages.AddNewLocation)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.DataEntry)]
public virtual void AddLocation()
{
var row = BAccount.Current;
if (row == null || row.BAccountID == null) return;
LocationMaint graph = null;
switch (row.Type)
{
case BAccountType.VendorType:
graph = PXGraph.CreateInstance<AP.VendorLocationMaint>();
break;
case BAccountType.CustomerType:
graph = PXGraph.CreateInstance<AR.CustomerLocationMaint>();
break;
default:
graph = PXGraph.CreateInstance<LocationMaint>();
break;
}
var newLocation = (Location)graph.Location.Cache.CreateInstance();
newLocation.BAccountID = row.BAccountID;
var locType = LocTypeList.CustomerLoc;
switch (row.Type)
{
case BAccountType.VendorType:
locType = LocTypeList.VendorLoc;
break;
case BAccountType.CombinedType:
locType = LocTypeList.CombinedLoc;
break;
}
newLocation.LocType = locType;
graph.Location.Insert(newLocation);
PXRedirectHelper.TryRedirect(graph, PXRedirectHelper.WindowMode.NewWindow);
}
I am using the below code to determine the activation of the Widnows7. I am getting 7 instances/product. I am not clear which product refers to original Windows 7.
I am unable to find documentation on to check which instance to determine whether Windows is activated or not
//use a SelectQuery to tell what we're searching in
SelectQuery searchQuery = new SelectQuery("SELECT * FROM SoftwareLicensingProduct");
//set the search up
ManagementObjectSearcher searcherObj = new ManagementObjectSearcher(scope, searchQuery);
//get the results into a collection
using (ManagementObjectCollection obj = searcherObj.Get())
{
foreach (ManagementObject m in obj)
{
if (Convert.ToInt32(m["GracePeriodRemaining"].ToString()) == 0)
{
MessageBox.Show("Windows is active");
break;
}
else
{
MessageBox.Show(" Windows is not active");
break;
}
}
//now loop through the collection looking for
//an activation status
}
When you use the SoftwareLicensingProduct WMI class, more than an instance is returned due to the Volume Licensing feature introduced in Windows Vista, so to return only the instance of you Windows version you must filter the WQL sentence using the PartialProductKey, ApplicationId and LicenseIsAddon properties.
Try this sample
using System;
using System.Collections.Generic;
using System.Management;
using System.Text;
namespace GetWMI_Info
{
class Program
{
static void Main(string[] args)
{
try
{
string ComputerName = "localhost";
ManagementScope Scope;
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), null);
Scope.Connect();
ObjectQuery Query = new ObjectQuery("SELECT * FROM SoftwareLicensingProduct Where PartialProductKey <> null AND ApplicationId='55c92734-d682-4d71-983e-d6ec3f16059f' AND LicenseIsAddon=False");
ManagementObjectSearcher Searcher = new ManagementObjectSearcher(Scope, Query);
foreach (ManagementObject WmiObject in Searcher.Get())
{
Console.WriteLine("{0,-35} {1,-40}", "Name", (String)WmiObject["Name"]);
Console.WriteLine("{0,-35} {1,-40}", "GracePeriodRemaining", (UInt32) WmiObject["GracePeriodRemaining"]);// Uint32
switch ((UInt32)WmiObject["LicenseStatus"])
{
case 0: Console.WriteLine("{0,-35} {1,-40}", "LicenseStatus", "Unlicensed");
break;
case 1: Console.WriteLine("{0,-35} {1,-40}", "LicenseStatus", "Licensed");
break;
case 2: Console.WriteLine("{0,-35} {1,-40}", "LicenseStatus", "Out-Of-Box Grace Period");
break;
case 3: Console.WriteLine("{0,-35} {1,-40}", "LicenseStatus", "Out-Of-Tolerance Grace Period");
break;
case 4: Console.WriteLine("{0,-35} {1,-40}", "LicenseStatus", "on-Genuine Grace Period");
break;
case 5: Console.WriteLine("{0,-35} {1,-40}", "LicenseStatus", "Notification");
break;
}
}
}
catch (Exception e)
{
Console.WriteLine(String.Format("Exception {0} Trace {1}",e.Message,e.StackTrace));
}
Console.WriteLine("Press Enter to exit");
Console.Read();
}
}
}
Another option is use the SLIsGenuineLocal function.
Try the answer to this question Determine Genuine Windows Installation in C# for a C# sample.
Windows® 7, VOLUME_KMSCLIENT channel refers to the actual product. this can found in description property
string ComputerName = "localhost";
ManagementScope Scope;
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), null);
Scope.Connect();
SelectQuery searchQuery = new SelectQuery("SELECT * FROM SoftwareLicensingProduct");
//set the search up
ManagementObjectSearcher searcherObj = new ManagementObjectSearcher(Scope, searchQuery);
//get the results into a collection
using (ManagementObjectCollection obj = searcherObj.Get())
{
foreach (ManagementObject m in obj)
{
String s = m["Description"].ToString();
if ((m["Description"].ToString()) .Contains("VOLUME_KMSCLIENT channel"))
{
if (m["LicenseStatus"].ToString() == "1")
{
var gracePeriod = Convert.ToInt32(m["GracePeriodRemaining"]) / (60 * 24);
MessageBox.Show("Not Licensed " + " Grace Period Remaining--" + gracePeriod.ToString() + "days");
}
else
{
var gracePeriod = Convert.ToInt32(m["GracePeriodRemaining"]) / (60 * 24);
MessageBox.Show("Not Licensed " + " Grace Period Remaining--" + gracePeriod.ToString() + "days");
}
}
}
}
I have a question relating to CRM DateTime problem. In opportunity form has custom DateTime field(Tender submission Date) that shows on form 'Date only' Format.
And other string field (Tender Date) that modifies date when Tender submission date changes. Let say...
Tender submission date is 29/06/2011 12:00:00
Tender Date should be 29/06/2012
I create the plug-in for Create Post-operation and Update Pre-operation. I retrieves TenderSubDate.Day, Month and Year.
Crm Time zone is (GMT+08:00) Kuala Lumpur,Singapore then want to change (GMT-06:00) Central Time(US & Canada).
The problem is that when i update Tender date based on Tender submission date, the program return one day less than or grater than Tender sub date. Let say..
First Secnario
Tender submission date is 29/06/2012 12:00:00am
Program returns 28/06/2012(it's wrong and it should be 29/06/2012)
Second secnario
Tender submission date is 1/08/2012 12:00:00am
Program returns 32/07/2012(it's wrong and it should be 1/08/2012)
what should i do in my program. please give me some idea. Here is my plug in code
public class TenderSubDateChange : IPlugin
{
#region Class Level Variables
//IServiceProvider _serviceProvider;
//IOrganizationServiceFactory _serviceFactory = null;
//IOrganizationService _service = null;
//IPluginExecutionContext _context = null;
Entity _target = null;
Entity _preImage = null;
Entity _postImage = null;
Guid _currentUser;
#endregion
#region IPlugin Members
public void Execute(IServiceProvider serviceProvider)
{
try
{
string message = null;
IPluginExecutionContext _context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
#region Organization Services
// Obtain the organization service reference.
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(_context.UserId);
#endregion
var ServiceContext = new OrganizationServiceContext(service);
_currentUser = _context.UserId;
message = _context.MessageName.ToLower();
if (message == "create")//message == "create" ||
{
if (_context.InputParameters.Contains("Target") && _context.InputParameters["Target"] != null)
_target = (Entity)_context.InputParameters["Target"];
if (_context.PreEntityImages.Contains("PreImage") && _context.PreEntityImages["PreImage"] != null)
_preImage = (Entity)_context.PreEntityImages["PreImage"];
if (_context.PostEntityImages.Contains("PostImage") && _context.PostEntityImages["PostImage"] != null)
_postImage = (Entity)_context.PostEntityImages["PostImage"];
DateTime hm_tenderdate;
if (_target.Attributes.Contains("hm_tendersubmissiondate"))
{
hm_tenderdate = (DateTime)_target.Attributes["hm_tendersubmissiondate"];
_target.Attributes["hm_tendersubdate"] = (hm_tenderdate.Day) + "/" + hm_tenderdate.Month + "/" + hm_tenderdate.Year;
service.Update(_target);
}
}
if (message == "update")//message == "create" ||
{
if (_context.InputParameters.Contains("Target") && _context.InputParameters["Target"] != null)
_target = (Entity)_context.InputParameters["Target"];
if (_context.PreEntityImages.Contains("PreImage") && _context.PreEntityImages["PreImage"] != null)
_preImage = (Entity)_context.PreEntityImages["PreImage"];
if (_context.PostEntityImages.Contains("PostImage") && _context.PostEntityImages["PostImage"] != null)
_postImage = (Entity)_context.PostEntityImages["PostImage"];
DateTime hm_tenderdate;
if (_target.Attributes.Contains("hm_tendersubmissiondate"))
{
hm_tenderdate = (DateTime)_target.Attributes["hm_tendersubmissiondate"];
_target.Attributes["hm_tendersubdate"] = (hm_tenderdate.Day) + "/" + hm_tenderdate.Month + "/" + hm_tenderdate.Year;
}
}
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException(ex.Message, ex);
}
}
#endregion
}
I faced this issue before and drove me crazy and the solution is to convert UTC time to local time
DateTime tenderDate = ((DateTime)target["new_tender"]).ToLocal();