I'm writing an invocablemethod batch apex class to rollup multiple currency fields on an "Order Package" object to its parent object of "Order Location". I thought I got everything added properly but it seems to be updating both the "Package_Monthly_Recurring_Fees__c" and the "Package_One_Time_Fees__c" to the same price from the Package_Monthly_Recurring_Fees__c field. Can someone help me identify why it's using the same price for both fields? Any help is much appreciated!
global class OrderLocationRollupSummary implements Database.Batchable<sObject>, Schedulable {
//Invocable Method
#InvocableMethod(label='Rollup All Order Packages to Locations')
global static void rollupAllorderpackages(List<Order_Location_Package__c> orderpackages) {
rollupOrderPackages(orderpackages);
}
//Batchable Methods
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([SELECT Id FROM Order_New_Location__c]);
}
global void execute(Database.BatchableContext context, List<sObject> batch){
Set<Id> OrderLocationIds = new Set<Id>();
for (sObject ordloc : batch) {
OrderLocationIds.add(ordloc.Id);
}
summarizeOrderPackages(OrderLocationIds);
}
global void finish(Database.BatchableContext context) {}
//Schedulable Methods
global void execute(SchedulableContext context){
OrderLocationRollupSummary batchJob = new OrderLocationRollupSummary();
Database.executeBatch(batchJob);
}
//Static Methods
public static void rollupOrderPackages(List<Order_Location_Package__c> orderpackages) {
Set<Id> OrderLocationIds = new Set<Id>();
//Get Order Location Ids from specified orderpackages
for (Order_Location_Package__c ordpckg : orderpackages) {
OrderLocationIds.add(ordpckg.New_Location_Name__c);
}
if (OrderLocationIds.isEmpty() == false) {
/*Execute as a future call so that the user doesn't have to wait around for
the rollup to finish. Unless, already in a future or batch call state then
just perform the rollup.*/
if (System.isFuture() == false && System.isBatch() == false) {
summarizeOrderPackagesAsync(OrderLocationIds);
}
else {
new OrderLocationRollupSummary().summarizeOrderPackages(OrderLocationIds);
}
}
}
#future
public static void summarizeOrderPackagesAsync(Set<Id> OrderLocationIds) {
new OrderLocationRollupSummary().summarizeOrderPackages(OrderLocationIds);
}
//Public Methods
public void summarizeOrderPackages(Set<Id> OrderLocationIds) {
//Get Order Locations to Update
List<Order_New_Location__c> orderlocations = queryOrderLocationsById(OrderLocationIds);
Map<Id, double> results = getOrderPackagesAmountsByLocationId(OrderLocationIds);
//Loop Order Locations and set Amounts
List<Order_New_Location__c> orderlocationsToUpdate = new List<Order_New_Location__c>();
for (Order_New_Location__c ordloc : orderlocations) {
double mrf = 0;
double otf = 0;
if (results.containsKey(ordloc.Id)) {
mrf = results.get(ordloc.Id);
otf = results.get(ordloc.Id);
}
//Determine if Amounts have Changed
if (ordloc.Package_Monthly_Recurring_Fees__c != mrf ||
ordloc.Package_One_Time_Fees__c != otf) {
ordloc.Package_Monthly_Recurring_Fees__c = mrf;
ordloc.Package_One_Time_Fees__c = otf;
orderlocationsToUpdate.add(ordloc); //Add location to collection to be updated
}
}
if(orderlocationsToUpdate.isEmpty() == false) {
Database.SaveResult[] saveResults = Database.update(orderlocationsToUpdate, false);
System.debug(saveResults);
}
}
//Private Methods
public Map<Id, double> getOrderPackagesAmountsByLocationId(Set<Id> OrderLocationIds) {
Map<Id, double> resultsByOrderLocationId = new Map<Id, double>();
//Summarize Order Package Amounts by Order Location Id
AggregateResult[] results = aggregateOrderPackageAmounts(OrderLocationIds);
for (AggregateResult result : results) {
Id orderlocationId = (Id) result.get('OrderLocation');
double mrf = (double) result.get('MRFees');
double otf = (double) result.get('OTFees');
resultsByOrderLocationId.put(orderlocationId, mrf);
resultsByOrderLocationId.put(orderlocationId, otf);
}
return resultsByOrderLocationId;
}
//Query Methods
private List<Order_New_Location__c> queryOrderLocationsById(Set<Id> OrderLocationIds) {
return [SELECT
Id
,Package_Monthly_Recurring_Fees__c
,Package_One_Time_Fees__c
FROM
Order_New_Location__c
WHERE
Id IN :OrderLocationIds];
}
private AggregateResult[] aggregateOrderPackageAmounts(Set<Id> OrderLocationIds) {
return [SELECT
New_Location_Name__c OrderLocation
,SUM(Monthly_Recurring_Fees__c) MRFees
,SUM(One_Time_Fees__c) OTFees
FROM
Order_Location_Package__c
WHERE
New_Location_Name__c IN :OrderLocationIds
GROUP BY
New_Location_Name__c];
}
}
Within your summarizeOrderPackages method, the following code is setting the 'mrf' and 'oft' variables to the same value.
if (results.containsKey(ordloc.Id)) {
mrf = results.get(ordloc.Id);
otf = results.get(ordloc.Id);
}
Afterwards, you are then setting the fields in question to the mrf and otf variables (which have the same value).
//Determine if Amounts have Changed
if (ordloc.Package_Monthly_Recurring_Fees__c != mrf ||
ordloc.Package_One_Time_Fees__c != otf) {
ordloc.Package_Monthly_Recurring_Fees__c = mrf;
ordloc.Package_One_Time_Fees__c = otf;
orderlocationsToUpdate.add(ordloc); //Add location to collection to be updated
}
Consider adding a wrapper and inner class within your OrderLocationRollupSummary class to solve this problem:
public class Order_New_Location_Wrapper {
Id orderlocationId = {get;set;}
Double mrf = {get;set;}
Double otf = {get;set;}
public class Order_New_Location_Wrapper (Id arg1, Double arg2, Double arg3)
orderlocationId = arg1;
mrf = arg2;
otf = arg3;
}
Then call this new inner class within your getOrderPackagesAmountsByLocationId method:
public Map<Id, Order_New_Location_Wrapper> getOrderPackagesAmountsByLocationId(Set<Id> OrderLocationIds) {
//create a list of orderlocations to represent the collection objects coming from the wrapper
resultsByOrderLocationId = new Map<Id, Order_New_Location_Wrapper>();
//Summarize Order Package Amounts by Order Location Id
AggregateResult[] results = aggregateOrderPackageAmounts(OrderLocationIds);
for (AggregateResult result : results) {
Id orderlocationId = (Id) result.get('OrderLocation');
double mrf = (double) result.get('MRFees');
double otf = (double) result.get('OTFees');
resultsByOrderLocationId.add(orderlocationId, new OpportunityWrapper(orderlocationId, mrg, otf));
}
return resultsByOrderLocationId;
}
You'll also need to update your summarizeOrderPackages method to be something like this:
public void summarizeOrderPackages(Set<Id> OrderLocationIds) {
//Get Order Locations to Update
List<Order_New_Location__c> orderlocations = queryOrderLocationsById(OrderLocationIds);
Map<Id, Order_New_Location_Wrapper> results = getOrderPackagesAmountsByLocationId(OrderLocationIds);
//Loop Order Locations and set Amounts
List<Order_New_Location__c> orderlocationsToUpdate = new List<Order_New_Location__c>();
for (Order_New_Location__c ordloc : orderlocations) {
double mrf = 0;
double otf = 0;
if (results.containsKey(ordloc.Id)) {
mrf = (Order_New_Location__c) results.get(ordloc.Id).mrf;
otf = (Order_New_Location__c) results.get(ordloc.Id).otf;
}
//Determine if Amounts have Changed
if (ordloc.Package_Monthly_Recurring_Fees__c != mrf ||
ordloc.Package_One_Time_Fees__c != otf) {
ordloc.Package_Monthly_Recurring_Fees__c = mrf;
ordloc.Package_One_Time_Fees__c = otf;
orderlocationsToUpdate.add(ordloc); //Add location to collection to be updated
}
}
if(orderlocationsToUpdate.isEmpty() == false) {
Database.SaveResult[] saveResults = Database.update(orderlocationsToUpdate, false);
System.debug(saveResults);
}
}
I was able to take care of this just by updating a few of my methods, see below. Thanks again for your help Robert!
global class OrderLocationRollupSummary implements Database.Batchable<sObject>, Schedulable {
//Invocable Method
#InvocableMethod(label='Rollup All Order Packages to Locations')
global static void rollupAllorderpackages(List<Order_Location_Package__c> orderpackages) {
rollupOrderPackages(orderpackages);
}
//Batchable Methods
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([SELECT Id FROM Order_New_Location__c]);
}
global void execute(Database.BatchableContext context, List<sObject> batch){
Set<Id> OrderLocationIds = new Set<Id>();
for (sObject ordloc : batch) {
OrderLocationIds.add(ordloc.Id);
}
summarizeOrderPackages(OrderLocationIds);
}
global void finish(Database.BatchableContext context) {}
//Schedulable Methods
global void execute(SchedulableContext context){
OrderLocationRollupSummary batchJob = new OrderLocationRollupSummary();
Database.executeBatch(batchJob);
}
//Static Methods
public static void rollupOrderPackages(List<Order_Location_Package__c> orderpackages) {
Set<Id> OrderLocationIds = new Set<Id>();
//Get Order Location Ids from specified orderpackages
for (Order_Location_Package__c ordpckg : orderpackages) {
OrderLocationIds.add(ordpckg.New_Location_Name__c);
}
if (OrderLocationIds.isEmpty() == false) {
/*Execute as a future call so that the user doesn't have to wait around for
the rollup to finish. Unless, already in a future or batch call state then
just perform the rollup.*/
if (System.isFuture() == false && System.isBatch() == false) {
summarizeOrderPackagesAsync(OrderLocationIds);
}
else {
new OrderLocationRollupSummary().summarizeOrderPackages(OrderLocationIds);
}
}
}
#future
public static void summarizeOrderPackagesAsync(Set<Id> OrderLocationIds) {
new OrderLocationRollupSummary().summarizeOrderPackages(OrderLocationIds);
}
//Public Methods
public void summarizeOrderPackages(Set<Id> OrderLocationIds) {
//Get Order Locations to Update
List<Order_New_Location__c> orderlocations = queryOrderLocationsById(OrderLocationIds);
Map<Id, AggregateResult> results = getOrderPackagesAmountsByLocationId(OrderLocationIds);
//Loop Order Locations and set Amounts
List<Order_New_Location__c> orderlocationsToUpdate = new List<Order_New_Location__c>();
for (Order_New_Location__c ordloc : orderlocations) {
double mrf = 0;
double otf = 0;
if (results.containsKey(ordloc.Id)) {
AggregateResult ar = results.get(ordloc.Id);
mrf = (double)ar.get('MRFees');
otf = (double)ar.get('OTFees');
}
//Determine if Amounts have Changed
if (ordloc.Package_Monthly_Recurring_Fees__c != mrf ||
ordloc.Package_One_Time_Fees__c != otf) {
ordloc.Package_Monthly_Recurring_Fees__c = mrf;
ordloc.Package_One_Time_Fees__c = otf;
orderlocationsToUpdate.add(ordloc); //Add location to collection to be updated
}
}
if(orderlocationsToUpdate.isEmpty() == false) {
Database.SaveResult[] saveResults = Database.update(orderlocationsToUpdate, false);
System.debug(saveResults);
}
}
//Private Methods
public Map<Id, AggregateResult> getOrderPackagesAmountsByLocationId(Set<Id> OrderLocationIds) {
Map<id,AggregateResult> resultsByOrderLocationId= new Map<Id,AggregateResult>();
//Summarize Order Package Amounts by Order Location Id
AggregateResult[] results = aggregateOrderPackageAmounts(OrderLocationIds);
for (AggregateResult result : results) {
Id orderlocationId = (Id) result.get('OrderLocation');
resultsByOrderLocationId.put(orderlocationId, result );
}
return resultsByOrderLocationId;
}
//Query Methods
private List<Order_New_Location__c> queryOrderLocationsById(Set<Id> OrderLocationIds) {
return [SELECT
Id
,Package_Monthly_Recurring_Fees__c
,Package_One_Time_Fees__c
FROM
Order_New_Location__c
WHERE
Id IN :OrderLocationIds];
}
private AggregateResult[] aggregateOrderPackageAmounts(Set<Id> OrderLocationIds) {
return [SELECT
New_Location_Name__c OrderLocation
,SUM(Monthly_Recurring_Fees__c) MRFees
,SUM(One_Time_Fees__c) OTFees
FROM
Order_Location_Package__c
WHERE
New_Location_Name__c IN :OrderLocationIds
GROUP BY
New_Location_Name__c];
}
}
Related
This is my batch class. I am facing a problem in the test class.
So please give me some solution on it. How to write this condition into class or how to write a test class for this.
I have this batch class created:
public class processBatch implements database.Batchable<object>, Database.AllowsCallouts, Scheduable{
public string AccountURL;
public String ProcessURL;
Public String Key;
private class JsonUpsertResult
{
List<Database.Error> Error {get;set;}
string SFDCId{get;set;}
String visibleID{get;set;}
Boolean is Created {get;set;}
Boolean isSuccess {get;set;}
}
public ProcessBatch(){
SAP_Details__c SAP = SAP_Details__c. getOrgDefaults();
AccountURL =SAP.Account_API_URL__c;
ProcessURL =SAP.process_API_URL__c;
Key =SAP.API_Key__c;
system.debug('API:' + ProcessURL + '-' + Key);
}
public Iterable<Object> start (Database.BatchableContext BC) {
List<Object> results =new List <Object>();
http http = new http();
httpRequest request =new HttpRequest();
system.debug('API:' '+ ProcessURL+' -' + Key);
request.setEndpoint(ProcessURL);
request.setMethod('POST');
request.setHeader('Authorization', Key);
request.setHeader('Accept', '*/*');
request.setHeader ('content-type', 'application/json');
}';
request.setBody(filters);
HttpResponse response =http.send(request);
if(responce.getStatusCode() == 200){
SAPProcessJsonApex2 gt = {SAPProcessJSON2Apex2) JSON.deserialize(response.getBody(), SAPProcessJSON2Apex.class);
List<SAPProcessJson2Apex.Result> res =gt.result;
system.debug('Size of Result: ' + res.size());
results = res;
}
return results;
}
public void execute (Database. BatchableContext BC, List<object> scope){
List<JSON2Apex.Results) accountResults = new List<JSON2Apex.Results>;
List<Process__c> ProcessToUpsert = new List<Process__c> ();
List<Process_Agent__c> CollectivesToupsert = new List<Process_Agent__c> ();
List<Department__c> departmentToupdate = new List<Department__c> ();
List<JSON2Apex.Results) revisedAccRes = new List JSON2Apex.Results> ();
List<SAPprocessJSON2Apex2.Results res = (List<SAPprocessJSO2Apex2.Results>) scope();
List<SAPprocessJSON2Apex2.Results revisedRes = nen List<SAPprocessJSONApex2.Results> ();
for (SAPprocessJSON2Apex2.Results r: res) {
SAPprocessJSON2Apex2.Document doc = r. document: List<SAPprocessJSON2 Apex2. Companies) comp = doc.companies;
List<SAPprocessJSON2Apex2.Companies> comp = doc.companies;
List<String> processTypes = doc.processTypes;
Lisk<String> categories = doc.categories;
Process__c pr = new Process__c ();
pr.Visibleprocess_No__c = doc.processId;
pr.Visible_method__c = 'SAP';
pr.process_Description__c = doc.description;
pr.Amount__c = doc.Amount;
pr.Construction_Type__c = doc.constructionType;
if(doc.phase == 'First planning'){
pr.Division=='First planning!';
} else if(doc.phase == 'Quotation'){
pr.Division == 'Main Step Quotation;
} else if(doc.phase = 'Contract Get') {
pr. Division== ' Main Step Agricultural';
}
pr.process_Full Name = doc.title;
pr.Name = doc.title.left (80);
if (categories.size() > 0){
if(categories [0] == 'Factory' || categories [0] = 'Storage' ){
pr.Sector__c = 'Industrial';
}else if(categories [0] == 'Packaging/Storage') {
pr. Type_of_Sector_c = 'Warehouse';
}else if(categories[0] == 'Shops and Retail' || categories [0]=='Any Showrooms') {
pr.Sector__c = 'Commercial';
}
if(pr.Sector__c != null && doc.Amount >= 50000){
ProcessToUpsert.ada (p);
revisedRes.ada (r);
} } }
public void finish (Database.BatchableContext BC) {
}
public void execute (SchedulableContext sc) {
database.executeBatch (new ProcessBatch (), 30);
}
}
&& d.Amount__c>= 50000 Whenever I write this condition in my class then my test class covers only 35%, and when I remove this condition it covers 80%. This condition is correct according to the requirement.
Test Class
#isIest (SeeAllData = true)
public class ProcessBatchTest {
#isTest public static void processBatchTest () {
Test.setMock (HttpCalloutMock.class, new HttpMock());
ProcessBatch pb = new ProcessBatch();
Test.startTest();
database.executeBatch (pb,30);
Test.stopTest();
You control the HttpMock class that pretends it returns results from SAP callout in the test. Nothing stops you from going there and making it return 2 records (1 with amount below 50K, 1 with amount above), or multiple statuses or whatever else you need.
I have a combobox that contains a list of scalar properties of an entity. I need to do a search on the property specified by the user in the combobox.
How do I reference the property when I have it as a string?
Something similar to DBSet<> for entity.
Example:
if (order.Firstname != null && order.Firstname.ToLower().Contains(textBoxSearch.Text.ToLower()))
In the above example, I need to replace Firstname with Surname or any other property, at runtime, depending on what the user selected.
You can always use reflection, for your case you'll need something along these lines:
static void Main(string[] args)
{
var entity = new Entity {
Height = 172,
FirstName = "Foo",
Birthday = new DateTime(1, 1, 1995)
};
var firstName = GetEntityProperty<string>(entity, "FirstName");
}
public static T GetEntityProperty<T>(object entity, string propertyName)
{
var type = entity.GetType();
var property = type.GetProperty(propertyName);
return (T)property.GetValue(entity);
}
Provided solution works, but it is not strongly-typed, so it is sensible to property renaming. A strongly-typed approach can be used, but it requires some setup. However, part of the setup is so generic that it can be reused:
public class Order
{
public String OrderNo { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public String Misc { get; set; }
}
// clearly define properties that allow search
public enum OrderSearchableProp
{
OrderNo = 1,
FirstName = 2,
LastName = 3
}
class Program
{
// strongly-type requires an explicit mapping between property and its expression within the order object
static Dictionary<OrderSearchableProp, Expression<Func<Order, String>>> OrderSearchableMap = new Dictionary<OrderSearchableProp, Expression<Func<Order, string>>>()
{
{ OrderSearchableProp.OrderNo, o => o.OrderNo },
{ OrderSearchableProp.FirstName, o => o.FirstName },
{ OrderSearchableProp.LastName, o => o.LastName },
};
// this gets a PropertyInfo based from an Expression. It should be placed into a generic utility class
// credit goes to Daniel - http://stackoverflow.com/a/17116267/2780791
public static PropertyInfo GetPropertyFromExpression<T, TProp>(Expression<Func<T, TProp>> GetPropertyLambda)
{
MemberExpression Exp = null;
//this line is necessary, because sometimes the expression comes in as Convert(originalexpression)
if (GetPropertyLambda.Body is UnaryExpression)
{
var UnExp = (UnaryExpression)GetPropertyLambda.Body;
if (UnExp.Operand is MemberExpression)
{
Exp = (MemberExpression)UnExp.Operand;
}
else
throw new ArgumentException();
}
else if (GetPropertyLambda.Body is MemberExpression)
{
Exp = (MemberExpression)GetPropertyLambda.Body;
}
else
{
throw new ArgumentException();
}
return (PropertyInfo)Exp.Member;
}
public static IList<Order> getFilteredOrders(int propFilterValue, IList<Order> orders, String needle)
{
var filterValue = (OrderSearchableProp)propFilterValue;
var filterProp = OrderSearchableMap[filterValue];
var lowNeedle = needle?.ToLower() ?? String.Empty;
return orders.Where(o =>
{
var propInfo = GetPropertyFromExpression<Order, String>(filterProp);
var propValue = (String) propInfo.GetValue(o) ?? String.Empty;
return propValue.ToLower().Contains(lowNeedle);
}).ToList();
}
static void Main(string[] args)
{
// can be used to populate the combo items
// otherwise, not used in this example
OrderSearchableProp[] enumValues = (OrderSearchableProp[])Enum.GetValues(typeof(OrderSearchableProp));
// test orders
var orderList = new List<Order>()
{
new Order() { OrderNo = "1234ABC", FirstName = "George", LastName = "Taylor", Misc = "Extra information"},
new Order() { OrderNo = "AB10", FirstName = "Anonymous", LastName = "X", Misc = "No comment"}
};
// test OrderNo search
var searchProp = (int) OrderSearchableProp.OrderNo;
var foundOrders = getFilteredOrders(searchProp, orderList, "ABC");
// test FirstName search
searchProp = (int)OrderSearchableProp.FirstName;
foundOrders = getFilteredOrders(searchProp, orderList, "a");
// test LastName search with empty string
searchProp = (int)OrderSearchableProp.LastName;
foundOrders = getFilteredOrders(searchProp, orderList, "");
// empty return
searchProp = (int)OrderSearchableProp.OrderNo;
foundOrders = getFilteredOrders(searchProp, orderList, null);
}
}
I have this Java class where I am writing the code for applying the overrides. I want to know if using ENUM is appropriate or if I need to use the switch case, how can I use it? Also, I have the for loop that I need to use as a common block of code for each override type. Apart from that, I do have few separate fields that I need to code for each override type.
public class EWFMService
{
private WorkbrainSystemAccessService wsa = new WorkbrainSystemAccessService();
private static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(EWFMService.class);
private final static String ovrCalcGrp = "ovrCalcGrp";
private DBConnection conn = null;
private int empId;
private Date ovrDate;
private String ovrTime;
private String ovrAction;
public List<EWFMServiceData> getProcessEWFMOverrides(String userName, String password, List<EWFMServiceInputData> inputData)
throws WSApplicationException{
logger.debug("EWFM Service");
wsa.logOn(userName, password);
List<EWFMServiceData> returnList = new ArrayList<EWFMServiceData> ();
logger.debug("userName = " + userName);
DBConnection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
conn = new DBConnection(ConnectionManager.getConnection());
for (int i = 0; i < inputData.size(); i++)
{
Here I want to retrieve the emp_id from the database, store the value in a variable and be able to use the variable in the rest of my program. How do I do it? To retrieve the emp_id, I am using the following query.
conn = new DBConnection(ConnectionManager.getConnection());
String sql = "SELECT EMP_ID FROM EMPLOYEE_HISTORY"
+ " WHERE EMP_VAL2 = **This is where I want to use the variable in which the values of emp_id will be stored. There can be more than 100 emp_ids**"
+ " AND SYSDATE BETWEEN EMPHIST_START_DATE AND EMPHIST_END_DATE";
EWFMServiceInputData inData = (EWFMServiceInputData) inputData.get(i);
OverrideType ot = OverrideType.getOverrideType(inData.getRecordType());
logger.debug("override type = " + ot.toString());
logger.debug("inputData ["+i+"] = " + inData.toString());
OverrideAccess oa = new OverrideAccess(conn);
OverrideData ovr = new OverrideData();
ovr.setOvrUdf4(inData.getReferenceId().toString());
if (ovrAction.equals("APPLY")) {
ovr.setOvrStatus(OverrideData.PENDING);
Here I want to determine the Action. If it is Apply, then I need to find out the recordType. So basically branch it out for each recordType using if else statements or enum as I believe switch doesn't support Java 1.5 which is what I am using. Then for each recordType, I branch out and write the appropriate code corresponding to that recordType. If Action is CANCEL, then I just write the following code.
} else if (ovrAction.equals("CANCEL")) {
String sql = "SELECT * FROM OVERRIDE"
+ " WHERE OVR_UDF4 = ?"
+ " AND OVRTYP_ID = ?";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()); {
ovr.assignByName(rs);
ovr.setUpdated(false);
ovr.setRetrieved(true);
ovr.setOvrStatus(OverrideData.CANCEL);
oa.save(ovr);
}
}
ovr.setEmpId(empId);
String strOvrDate = inData.getOvrStartDate();
ovr.setOvrStartDate(DateHelper.parseDate(strOvrDate, "MM/dd/yyyy"));
if (ovrStartTime != null) {
ovr.setOvrStartTime(ovrTime);
}
Object ovrEndDate;
if (ovrEndDate != null) {
ovr.setOvrEndDate(ovrDate);
}
Object ovrEndTime;
if (ovrEndTime!= null) {
ovr.setOvrEndTime(ovrTime);
}
ovr.setOvrComment(inData.getOvrComments());
ovr.setWbuName(inData.getWbuName());
ovr.setWbuNameActual(inData.getWbuNameActual());
ovr.setOvrNewValue("VAC");
ovr.setOvrCreateDate(new Date());
ovr.setOvrtypId(103);
oa.insert(ovr);
RuleEngine.runCalcGroup(conn,
empId,
ovrDate,
ovrDate);
//COMMON BLOCK ENDS HERE
EWFMServiceData outData = new EWFMServiceData();
outData.setReferenceId(inData.getReferenceId());
String [] status = {"SUCCESS", "ERROR", "LOCKED", "EXCEPTION"};
Random ran = new Random();
String gen = status[ran.nextInt(status.length)];
logger.debug("Status is" + status );
outData.setStatus(gen);
if (gen.equals("SUCCESS")){
outData.setErrorDetails("");
} else if (gen.equals("ERROR")) {
outData.setErrorDetails("Usage of time code VAC is not allowed; balance is insufficient." + " error");
} else if (gen.equals("LOCKED")) {
outData.setErrorDetails("Timesheet cannot be edited because it is locked for payroll close." + "locked");
} else if (gen.equals("EXCEPTION")) {
outData.setErrorDetails("{ML}QR_INCORRECT_CONDITION_PARAMETER{/ML}Error in condition AWA Is Self Override Condition: java.lang.NullPointerException{ARGS}AWA Is Self Override Conditionjava.lang.NullPointerException{/ARGS" + "exception");
}
returnList.add(outData);
}
}catch (Exception e){
logger.error("Error occured",e);
throw new WSApplicationException("Error retrieved",e);
}finally{
SQLUtil.cleanUp(conn, ps, rs);
}
wsa.logOff();
logger.debug("inputData+ ");
return returnList;
}
// I need to know if writing enum is okay or can I just write a switch case above in the for loop and branch each override type and declare their individual variables there? What's the best way? Can someone help me with the code?
public enum OverrideType {
WORKDETAIL,
WORKPREMIUM,
EMPLOYEESCHEDULE,
EMPLOYEE;
public static OverrideType getOverrideType(String recordType) {
if(recordType == null) {
throw new IllegalArgumentException("Record Type cannot be null");
}
if(recordType.equals("Work Detail")) {
return WORKDETAIL;
} else if (recordType.equals("Work Premium")) {
return WORKPREMIUM;
} else if (recordType.equals("Schedule")) {
return EMPLOYEESCHEDULE;
} else if (recordType.equals("Shift Pattern")) {
return EMPLOYEE;
} else {
throw new IllegalArgumentException("Record Type cannot be" + recordType);
}
}
}
}
THE OTHER FIELDS I NEED TO INCLUDE ARE AS FOLLOWS:
FOR WORKDETAIL, I NEED TO USE TIMECODE OF FORMAT THAT IS SENT BY THE CLIENT.
FOR WORK PREMIUM, I NEED TO USE TIMECODE OF FORMAT THAT IS SENT BY THE CLIENT AND ANOTHER FIELD IS MINUTES THAT GIVES THE NUMBER OF MINUTES WHICH IS ALSO SENT BY THE CLIENT.
Generally, using enums is appropriate, especially if you have a defined set of possible types.
You can also add behavior to the enums, which could make your enum a little bit more sophisticated:
public enum OverrideType {
WORKDETAIL("Work Detail"),
WORKPREMIUM("Work Premium"),
EMPLOYEESCHEDULE("Schedule"),
EMPLOYEE("Shift Pattern");
private String identifier;
private OverrideType(String identifier){
this.identifier = identifier;
}
public static OverrideType getOverrideType(String recordType) {
if(recordType == null) {
throw new IllegalArgumentException("Record Type cannot be null");
}
for (OverrideType ot : OverrideType.values()) {
if (recordType.equals(ot.identifier)) {
return ot;
}
}
return null;
}
}
The following example shows how to use an interface in enums or an abstract method definition:
public enum OverrideType implements OverrideTypeIF {
WORKDETAIL("Work Detail") {
public int getKey() {
return 0;
}
},
WORKPREMIUM("Work Premium") {
public int getKey() {
return 0;
}
},
EMPLOYEESCHEDULE("Schedule") {
public int getKey() {
return 0;
}
},
EMPLOYEE("Shift Pattern") {
public int getKey() {
return 0;
}
public void myInterfaceMethod() {
// do type specific behavior
}
};
private String identifier;
private OverrideType(String identifier){
this.identifier = identifier;
}
public abstract int getKey();
public void myInterfaceMethod() {
// do default behavior
}
public static OverrideType getOverrideType(String recordType) {
if(recordType == null) {
throw new IllegalArgumentException("Record Type cannot be null");
}
for (OverrideType ot : OverrideType.values()) {
if (recordType.equals(ot.identifier)) {
return ot;
}
}
return null;
}
}
public interface OverrideTypeIF {
void myInterfaceMethod();
}
I have a situation where a BindingList<> represents a collection of POCOs that have sub-collections of similar nature, Here is a sample code of two such POCOs and their respective lists:
The DirectoryTypePoco
public class DirectoryTypePoco : IBasePoco
{
public DirectoryTypePoco()
{
}
public DirectoryTypePoco(Int16 directoryTypeId, String directoryImplementation, String directoryDescription, DirectoryDefinitionPocoList directoryDefinition)
{
DirectoryTypeId = directoryTypeId;
DirectoryImplementation = directoryImplementation;
DirectoryDescription = directoryDescription;
DirectoryDefinition = directoryDefinition;
}
public Int16 DirectoryTypeId { get; set; }
public String DirectoryImplementation { get; set; }
public String DirectoryDescription { get; set; }
public DirectoryDefinitionPocoList DirectoryDefinition { get; set; }
public object GenerateEntity(GenericRepository repository, params object[] parameters)
{
var lastMaxEntityId = repository.GetQuery<DirectoryType>().Select(select => #select.DirectoryTypeId).DefaultIfEmpty().Max();
var newEntity = new DirectoryType
{
DirectoryTypeId = (short)(lastMaxEntityId + 1),
DirectoryImplementation = this.DirectoryImplementation,
DirectoryDescription = this.DirectoryDescription
};
return newEntity;
}
}
And the BindingList<DirectoryTypePoco>:
public class DirectoryTypePocoList : BindingList<DirectoryTypePoco>
{
public DirectoryTypePocoList()
{
using (var repository = new GenericRepository(new PWRDbContext()))
{
var query = repository.GetQuery<DirectoryType>();
foreach (var r in query)
{
Add(new DirectoryTypePoco(r.DirectoryTypeId, r.DirectoryImplementation, r.DirectoryDescription, new DirectoryDefinitionPocoList(r.DirectoryTypeId)));
}
}
}
public DirectoryTypePocoList(short directoryTypeId)
{
using (var repository = new GenericRepository(new PWRDbContext()))
{
var query = repository.GetQuery<DirectoryType>(where => where.DirectoryTypeId == directoryTypeId);
foreach (var r in query)
{
Add(new DirectoryTypePoco(r.DirectoryTypeId, r.DirectoryImplementation, r.DirectoryDescription, new DirectoryDefinitionPocoList(r.DirectoryTypeId)));
}
}
}
}
The second object: DirectoryDefinitionPoco
public class DirectoryDefinitionPoco : IBasePoco
{
public DirectoryDefinitionPoco()
{
}
public DirectoryDefinitionPoco(Int16 directoryTypeId, Byte parameterId, String parameterName, String parameterValidation, Boolean encryptionRequired, PocoChangeType changeType = PocoChangeType.None)
{
DirectoryTypeId = directoryTypeId;
ParameterId = parameterId;
ParameterName = parameterName;
ParameterDescription = parameterName;
ParameterRequired = false;
ParameterValidation = parameterValidation;
EncryptionRequired = encryptionRequired;
}
public Int16 DirectoryTypeId { get; set; }
public Byte ParameterId { get; set; }
public String ParameterName { get; set; }
public String ParameterDescription { get; set; }
public String ParameterValidation { get; set; }
public Boolean ParameterRequired { get; set; }
public Boolean EncryptionRequired { get; set; }
public object GenerateEntity(GenericRepository repository, params object[] parameters)
{
var masterId = (short) parameters[0];
var lastMaxEntityId = repository.GetQuery<DirectoryDefinition>(where => where.DirectoryTypeId == masterId).Select(select => #select.ParameterId).DefaultIfEmpty().Max();
var newEntity = new DirectoryDefinition
{
DirectoryTypeId = (short)parameters[0],
ParameterId = (byte)(lastMaxEntityId + 1),
ParameterName = this.ParameterName,
ParameterDescription = this.ParameterDescription,
ParameterValidation = this.ParameterValidation,
ParameterRequired = this.ParameterRequired,
EncryptionRequired = this.EncryptionRequired
};
return newEntity;
}
}
And BindingList<DirectoryDefinitionPoco>:
public class DirectoryDefinitionPocoList : BindingList<DirectoryDefinitionPoco>
{
public DirectoryDefinitionPocoList(short directoryTypeId)
{
using (var repository = new GenericRepository(new PWRDbContext()))
{
var query = repository.GetQuery<DirectoryDefinition>(where => where.DirectoryTypeId == directoryTypeId);
foreach (var r in query)
{
Add(new DirectoryDefinitionPoco(r.DirectoryTypeId, r.ParameterId, r.ParameterName, r.ParameterValidation, r.EncryptionRequired));
}
}
}
public List<DirectoryDefinition> GetSourceQuery()
{
List<DirectoryDefinition> result;
using (var repository = new GenericRepository(new PWRDbContext()))
{
result = repository.GetQuery<DirectoryDefinition>().ToList();
}
return result;
}
public List<DirectoryDefinition> GetSourceQuery(short directoryTypeId)
{
List<DirectoryDefinition> result;
using (var repository = new GenericRepository(new PWRDbContext()))
{
result = repository.GetQuery<DirectoryDefinition>(where => where.DirectoryTypeId == directoryTypeId).ToList();
}
return result;
}
}
On the form, I load the data into the grid through a BindingSource component. The child rows are added properly and the data is valid.
Here is the issue: I'm able to add new DirectoryTypePoco but when try to add a DirectoryDefinitionPoco, in the code, the the DirectoryDefinitionPocoobject that I get has a zero for it's parent object. In the above picture, the Test5.dll234 is a DirectoryTypePoco with DirectoryTypeId = 8 and all child under it are ok except the new one I create. What am I suppose to do to make sure I have Master-Child relation in this case?
Ok. It seems that there are two thing I should have noticed in my design.
The individual child Poco needs to know the parent Poco through a reference.
The DevExpress Grid has methods that allow for retrieving the attached data to a parent row while in the child view' particular row.
The first part is straightforwards: add a new property in the child poco of parent poco type.
This however, in my case, doesn't solve my issue as when I visually add a new row on the grid, the default constructor is invoked and it takes no parameters and hence the parent poco reference will remain NULL and the Ids (numeric) will be defaulted to 0
The second point helped fix my issue completely. I was able to conjure up an extension method for the XtraGrid's GridView as follows:
public static class DevExpressGridHelper
{
public static IBasePoco GetPocoFromSelectedRow(this BaseView view)
{
return (IBasePoco)view.GetRow(((GridView)view).FocusedRowHandle);
}
public static IBasePoco GetParentPocoFromSelectedRow(this GridView view)
{
if (view.ParentView !=null)
{
// return (IBasePoco)(view.ParentView).GetRow(((GridView)(view.ParentView)).FocusedRowHandle);
return (IBasePoco)((GridView)view.ParentView).GetFocusedRow();
}
return null;
}
}
And used it as follows:
private void GridMain_Level_1_RowUpdated(object sender, RowObjectEventArgs e)
{
var view = sender as GridView;
if (view == null)
{
return;
}
var pocoObject = e.Row as DirectoryDefinitionPoco;
if (pocoObject == null)
{
return;
}
var parentPocoObject = view.GetParentPocoFromSelectedRow();
if (parentPocoObject == null)
{
return;
}
if (view.IsNewItemRow(e.RowHandle))
{
Create(pocoObject, parentPocoObject);
}
else
{
Update(pocoObject);
}
}
I want to filter a ObservableCollection with max 3000 items in a DataGrid with 6 columns. The user should be able to filter in an "&&"-way all 6 columns.
Should I use LINQ or a CollectionView for it? LINQ seemed faster trying some www samples. Do you have any pro/cons?
UPDATE:
private ObservableCollection<Material> _materialList;
private ObservableCollection<Material> _materialListInternal;
public MaterialBrowserListViewModel()
{
_materialListInternal = new ObservableCollection<Material>();
for (int i = 0; i < 2222; i++)
{
var mat = new Material()
{
Schoolday = DateTime.Now.Date,
Period = i,
DocumentName = "Excel Sheet" + i,
Keywords = "financial budget report",
SchoolclassCode = "1",
};
_materialListInternal.Add(mat);
var mat1 = new Material()
{
Schoolday = DateTime.Now.Date,
Period = i,
DocumentName = "Word Doc" + i,
Keywords = "Economical staticstics report",
SchoolclassCode = "2",
};
_materialListInternal.Add(mat1);
}
MaterialList = CollectionViewSource.GetDefaultView(MaterialListInternal);
MaterialList.Filter = new Predicate<object>(ContainsInFilter);
}
public bool ContainsInFilter(object item)
{
if (String.IsNullOrEmpty(FilterKeywords))
return true;
Material material = item as Material;
if (DocumentHelper.ContainsCaseInsensitive(material.Keywords,FilterKeywords,StringComparison.CurrentCultureIgnoreCase))
return true;
else
return false;
}
private string _filterKeywords;
public string FilterKeywords
{
get { return _filterKeywords; }
set
{
if (_filterKeywords == value)
return;
_filterKeywords = value;
this.RaisePropertyChanged("FilterKeywords");
MaterialList.Refresh();
}
}
public ICollectionView MaterialList { get; set; }
public ObservableCollection<Material> MaterialListInternal
{
get { return _materialListInternal; }
set
{
_materialListInternal = value;
this.RaisePropertyChanged("MaterialList");
}
}
Using ICollectionView gives you automatic collection changed notifications when you call Refresh. Using LINQ you'll need to fire your own change notifications when the filter needs to be re-run to update the UI. Not difficult, but requires a little more thought than just calling Refresh.
LINQ is more flexible that the simple yes/no filtering used by ICollectionView, but if you're not doing something complex there's not really any advantage to that flexibility.
As Henk stated, there shouldn't be a noticable performance difference in the UI.
For an interactive (DataGrid?) experience you should probabaly use the CollectionView. For a more code-oriented sorting, LINQ.
And with max 3000 items, speed should not be a (major) factor in a UI.
How about both? Thomas Levesque built a LINQ-enabled wrapper around ICollectionView.
Usage:
IEnumerable<Person> people;
// Using query comprehension
var query =
from p in people.ShapeView()
where p.Age >= 18
orderby p.LastName, p.FirstName
group p by p.Country;
query.Apply();
// Using extension methods
people.ShapeView()
.Where(p => p.Age >= 18)
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName)
.Apply();
Code:
public static class CollectionViewShaper
{
public static CollectionViewShaper<TSource> ShapeView<TSource>(this IEnumerable<TSource> source)
{
var view = CollectionViewSource.GetDefaultView(source);
return new CollectionViewShaper<TSource>(view);
}
public static CollectionViewShaper<TSource> Shape<TSource>(this ICollectionView view)
{
return new CollectionViewShaper<TSource>(view);
}
}
public class CollectionViewShaper<TSource>
{
private readonly ICollectionView _view;
private Predicate<object> _filter;
private readonly List<SortDescription> _sortDescriptions = new List<SortDescription>();
private readonly List<GroupDescription> _groupDescriptions = new List<GroupDescription>();
public CollectionViewShaper(ICollectionView view)
{
if (view == null)
throw new ArgumentNullException("view");
_view = view;
_filter = view.Filter;
_sortDescriptions = view.SortDescriptions.ToList();
_groupDescriptions = view.GroupDescriptions.ToList();
}
public void Apply()
{
using (_view.DeferRefresh())
{
_view.Filter = _filter;
_view.SortDescriptions.Clear();
foreach (var s in _sortDescriptions)
{
_view.SortDescriptions.Add(s);
}
_view.GroupDescriptions.Clear();
foreach (var g in _groupDescriptions)
{
_view.GroupDescriptions.Add(g);
}
}
}
public CollectionViewShaper<TSource> ClearGrouping()
{
_groupDescriptions.Clear();
return this;
}
public CollectionViewShaper<TSource> ClearSort()
{
_sortDescriptions.Clear();
return this;
}
public CollectionViewShaper<TSource> ClearFilter()
{
_filter = null;
return this;
}
public CollectionViewShaper<TSource> ClearAll()
{
_filter = null;
_sortDescriptions.Clear();
_groupDescriptions.Clear();
return this;
}
public CollectionViewShaper<TSource> Where(Func<TSource, bool> predicate)
{
_filter = o => predicate((TSource)o);
return this;
}
public CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, true, ListSortDirection.Ascending);
}
public CollectionViewShaper<TSource> OrderByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, true, ListSortDirection.Descending);
}
public CollectionViewShaper<TSource> ThenBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, false, ListSortDirection.Ascending);
}
public CollectionViewShaper<TSource> ThenByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, false, ListSortDirection.Descending);
}
private CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector, bool clear, ListSortDirection direction)
{
string path = GetPropertyPath(keySelector.Body);
if (clear)
_sortDescriptions.Clear();
_sortDescriptions.Add(new SortDescription(path, direction));
return this;
}
public CollectionViewShaper<TSource> GroupBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
string path = GetPropertyPath(keySelector.Body);
_groupDescriptions.Add(new PropertyGroupDescription(path));
return this;
}
private static string GetPropertyPath(Expression expression)
{
var names = new Stack<string>();
var expr = expression;
while (expr != null && !(expr is ParameterExpression) && !(expr is ConstantExpression))
{
var memberExpr = expr as MemberExpression;
if (memberExpr == null)
throw new ArgumentException("The selector body must contain only property or field access expressions");
names.Push(memberExpr.Member.Name);
expr = memberExpr.Expression;
}
return String.Join(".", names.ToArray());
}
}
Credit:
http://www.thomaslevesque.com/2011/11/30/wpf-using-linq-to-shape-data-in-a-collectionview/
Based on a visual complexity and number of items there really WILL be a noticable performance difference since the Refresh method recreates the whole view!!!
You need my ObservableComputations library. Using this library you can code like this:
ObservableCollection<Material> MaterialList = MaterialListInternal.Filtering(m =>
String.IsNullOrEmpty(FilterKeywords)
|| DocumentHelper.ContainsCaseInsensitive(
material.Keywords, FilterKeywords, StringComparison.CurrentCultureIgnoreCase));
MaterialList reflects all the changes in the MaterialListInternal collection. Do not forget to add the implementation of the INotifyPropertyChanged interface to Material class, so that MaterialList collection reflects the changes in material.Keywords property.