Optaplanner ScoreDirector continuous planning DayOff Request - optaplanner

I am trying to enable continuous planning and requests. I have tried to follow the computer example provided within the doco. Code below. I can see the values updated during debug. Code below. What am I doing wrong
public void addEmployeeDayOff(final Employee employee,final DayOffRequest dayOffRequest) {
logger.info("Scheduling employee dayoff ({}).", dayOffRequest);
doProblemFactChange(scoreDirector -> {
NurseRoster nurseRoster = scoreDirector.getWorkingSolution();
Employee workingEmployee = scoreDirector.lookUpWorkingObject(employee);
DayOffRequest dayoffRequest = scoreDirector.lookUpWorkingObject(dayOffRequest);
scoreDirector.beforeProblemPropertyChanged(workingEmployee);
if (workingEmployee == null) {
return;
}
ArrayList<DayOffRequest> requestoffList = new ArrayList<>(nurseRoster.getDayOffRequestList());
nurseRoster.setDayOffRequestList(requestoffList);
scoreDirector.beforeProblemFactAdded(dayoffRequest);
workingEmployee.getDayOffRequestMap().put(dayoffRequest.getShiftDate(), dayoffRequest);
scoreDirector.afterProblemPropertyChanged(workingEmployee);
nurseRoster.getDayOffRequestList().add(dayoffRequest);
scoreDirector.afterProblemPropertyChanged(dayoffRequest);
scoreDirector.triggerVariableListeners();
});
The above is called from here:
List<DayOffRequest> dayOffRequestList;
//updating from database .. not sure if this is the issue
List<DayOffData> dayOffElementList = (List<DayOffData>) rosterService.listDayOffData();
dayOffRequestList = new ArrayList<>(dayOffElementList.size());
long nextdayoffId =0L;
for (DayOffData element : dayOffElementList) {
if (nextdayoffId <= element.getId()) {
nextdayoffId = element.getId() + 1L;
}
DayOffRequest dayOffRequest = new DayOffRequest();
String empID = element.getEmployee().getName();
int weight = element.getWeight();
LocalDate shiftDate = element.getShiftDate();
ShiftDate date1 = shiftDateMap.get(shiftDate);
Employee employee = employeeMap.get(empID);
dayOffRequest.setId(nextdayoffId);
dayOffRequest.setWeight(weight);
dayOffRequest.setEmployee(employee);
dayOffRequest.setShiftDate(date1);
dayOffRequestList.add(dayOffRequest);
//If this is not enabled the values don't pass to the addEmployeeDayOff method is this correct??
scoreDirector.afterProblemFactAdded(dayOffRequest);
scoreDirector.afterProblemFactAdded(employee);
addEmployeeDayOff(employee,dayOffRequest);
}

I found this worked but not sure if this is the correct way to approach the problem.
public void addEmployeeDayOff(final Employee employee, final DayOffRequest dayOffRequest) {
logger.info("Scheduling employee dayoff ({}).", dayOffRequest);
doProblemFactChange(scoreDirector -> {
NurseRoster nurseRoster = (NurseRoster) scoreDirector.getWorkingSolution();
Employee workingEmployee = scoreDirector.lookUpWorkingObject(employee);
DayOffRequest dayoffRequest = (DayOffRequest) scoreDirector.lookUpWorkingObject(dayOffRequest);
scoreDirector.beforeProblemPropertyChanged(workingEmployee);
if (workingEmployee == null) {
return;
}
ArrayList<DayOffRequest> requestoffList = new ArrayList<>(nurseRoster.getDayOffRequestList());
nurseRoster.setDayOffRequestList(requestoffList);
scoreDirector.afterProblemFactAdded(requestoffList);
scoreDirector.afterProblemPropertyChanged(requestoffList);
ArrayList<Employee> beforeempList = new ArrayList<>(nurseRoster.getEmployeeList());
nurseRoster.setEmployeeList(beforeempList);
nurseRoster.getEmployeeList().remove(workingEmployee);
scoreDirector.beforeProblemFactRemoved(workingEmployee);
scoreDirector.afterProblemFactRemoved(workingEmployee);
ArrayList<Employee> empList = new ArrayList<>(nurseRoster.getEmployeeList());
nurseRoster.setEmployeeList(empList);
workingEmployee.getDayOffRequestMap().put(dayOffRequest.getShiftDate(), dayOffRequest);
nurseRoster.getEmployeeList().add(workingEmployee);
scoreDirector.beforeProblemFactAdded(workingEmployee);
scoreDirector.afterProblemFactAdded(workingEmployee);
scoreDirector.triggerVariableListeners();
});

I have uploaded the xml and Java file here https://github.com/rod182211/Optaplanner in to hope someone can advise why when I advance 14 days not all rule continue to apply. I assumed scoredirector only needed to be made aware of changes.

Related

Understanding the entity context lifetime

I have a error in my code and I checked several related errors in Stackoverflow and their solution. Yet, I could not understand a few concepts.
First of all this is the code section where the error shows up:
Controller:
public ActionResult IndexPost(IEnumerable<QuestionView> questions, string button)
{
foreach (QuestionView question in questions)
{
UserAnswerRepository useransrepo = new UserAnswerRepository();
MiscDBRepository miscrepo = new MiscDBRepository();
//Store user's answer
useransrepo.CreateUserAnswer(<>);
//Check answer against rubrics
choiceList = miscrepo.GetChoicesAnswithQuesId(question.QuestionId);
....
}
}
DB Repository:
public class UserAnswerRepository
{
//Add user answer to DB
public void CreateUserAnswer(string sessionId, string email, int questionId, string answer)
{
using (SWSSEntities db = new SWSSEntities())
{
user_answers useranstable = new user_answers();
AuthCodeRepository authrepo = new AuthCodeRepository();
UserRepository userrepo = new UserRepository();
QuestionRepository quesrepo = new QuestionRepository();
if (sessionId == "0")
useranstable.authcode = authrepo.GetRecordwithEmail(email);
else
//useranstable.user = userrepo.GetUser(userrepo.GetUserNameByEmail(email));
useranstable.question = quesrepo.GetQuestionRecordwithId(questionId);
useranstable.answer = answer;
db.AddTouser_answers(useranstable);
db.SaveChanges();
}
}
}
and
public class MiscDBRepository
{
//Get choices and answers for a question with questionid
public List<string> GetChoicesAnswithQuesId(int id)
{
using (SWSSEntities _db = new SWSSEntities())
{
var list = new List<string>();
list.Add((from d in _db.question_choices where d.question.id == id select d.choice1).First());
list.Add((from d in _db.question_choices where d.question.id == id select d.choice2).First());
list.Add((from d in _db.question_choices where d.question.id == id select d.choice3).First());
list.Add((from d in _db.question_choices where d.question.id == id select d.choice4).First());
list.Add((from d in _db.question_choices where d.question.id == id select d.answer).First());
return list;
}
}
}
Question 1:
I am getting the following error: "The EntityKey property can only be set when the current value of the property is null."
In the controller, I am using the object creation inside the "for" loop, so the object should be created afresh for every loop. Yet, why would I get the error that the EntityKey property is already set in the following lines:
public void AddTouser_answers(user_answers user_answers)
{
base.AddObject("user_answers", user_answers);
}
Isn't the object getting created afresh for every loop? Is it because of this statement: "using (SWSSEntities _db = new SWSSEntities())"
Question 2:
There is a famous error been discussed in Stackoverflow:"an entity object cannot be referenced by multiple instances of ientitychangetracker".
I am curious that these two lines,
UserAnswerRepository useransrepo = new UserAnswerRepository();
MiscDBRepository miscrepo = new MiscDBRepository();
did not throw that error. Why is that?
Question 3:
How should I resolve this issue?
My experiments:
I tried to remove the "using (SWSSEntities _db = new SWSSEntities())" statement from the repositories and created entity context in each. Then the above error in Question 2 was thrown. So, "using (SWSSEntities _db = new SWSSEntities())" binds and unbinds my entity properly (correct me if I am wrong) between the two repositories instantiation.
If I am right, why wouldn't it do the same for the error in "Question 1" (consecutive instantiation of the same repository).
Is it because UserAnswerRepository is saving to DB and the other one is just reading?
I am confused. I am pretty new to .NET C# and this whole Entity Framework thing. So pls excuse, if I was being silly.
DB Schema:

Exception in Entity Framework: Error 0194: All artifacts loaded

This is a c#/asp.net project. The full error message I get is:Error 0194: All artifacts loaded into the item collection must have the same version. Multiple versions were encountered.
This project was started as a 3.5 and upgraded to 4.0. When I try to test any of the methods I get the error that I posted in the subject line. I am going to include the actual lines that it throws the exception on. If there is anything in people need to see to try to help let me know and I post it as well. Any help will be appreciated, I am having no luck with this.
/// <summary>
/// Initializes a new SFBExternalPaymentsEntities object using the connection string found in the 'SFBExternalPaymentsEntities' section of the application configuration file.
/// </summary>
public SFBExternalPaymentsEntities() : base("name=SFBExternalPaymentsEntities", "SFBExternalPaymentsEntities")
{
this.ContextOptions.LazyLoadingEnabled = false;
OnContextCreated();
}
/// <summary>
/// Initialize a new SFBExternalPaymentsEntities object.
/// </summary>
public SFBExternalPaymentsEntities(string connectionString) : base(connectionString, "SFBExternalPaymentsEntities")
{
this.ContextOptions.LazyLoadingEnabled = false;
OnContextCreated();
}
/// <summary>
/// Initialize a new SFBExternalPaymentsEntities object.
/// </summary>
public SFBExternalPaymentsEntities(EntityConnection connection) : base(connection, "SFBExternalPaymentsEntities")
{
this.ContextOptions.LazyLoadingEnabled = false;
OnContextCreated();
}
#endregion
Added a method calling the constructor.
public static CreditCardResponse AuthCapture(CreditCard newCC)
{
ACHResponse validateResponse = CreditCard.Validate(newCC);
if (validateResponse.Status == "Accepted")
{
Profile currentProfile = new Profile();
currentProfile = ProfilesGateWay.GetByID(newCC.ProfileID);
CreditCardTransaction newCCTransaction = CreateCreditCardTransaction(newCC, currentProfile);
ServiceClient client = new ServiceClient();
CreditCardTransactionResponse cctResponse = client.CreditCardAuthorizeAndCapture(newCCTransaction);
client.Close();
CreditCardResponse ccResponse = CreateCCResponse(cctResponse);
if (ccResponse.ResponseCode == 1)
{
int authAVS = ConvertAVStoInt(ccResponse.AVSResponse);
int appAVS = ConvertAVStoInt(newCC.AVLevel);
bool isAVSPass = CompareAVS(authAVS, appAVS);
if (isAVSPass == false)
{
ccResponse.ResponseCode = 0;
ccResponse.ResponseReasonCode = 99;
ccResponse.ResponseText = "Did not meet your AVS requirements";
return ccResponse;
}
else
{
int newCCID = CreateCreditCardRecord(newCC, currentProfile, cctResponse, "Captured", "Auth_Capture");
CreditCardRecord updateCC = CreditCardRecordsGateWay.GetByID(newCCID);
updateCC.CaptureOn = DateTime.Now;
CreditCardRecordsGateWay.Update(updateCC);
return ccResponse;
}
}
else
{
return ccResponse;
}
}
CreditCardResponse newCCResponse = new CreditCardResponse();
newCCResponse.ResponseCode = 0;
newCCResponse.AchResponse = validateResponse;
return newCCResponse;
}
public static CreditCardResponse PriorAuthCapture(CreditCard newCC)
{
CreditCardRecord ccRecord = CreditCardRecordsGateWay.GetByCCGateWayID(newCC.CreditCardTransactionID);
ServiceClient client = new ServiceClient();
CreditCardTransaction ccTransaction = client.CreditCardGetTransactionById(ccRecord.CCGatewayID);
CreditCardTransactionResponse cctResponse = client.CreditCardPriorAuthorizationCapture(ccTransaction);
if (cctResponse.ResponseCode == 1)
{
ccRecord.Status = "Captured";
ccRecord.CaptureOn = DateTime.Now;
}
CreditCardResponse ccResponse = CreateCCResponse(cctResponse);
return ccResponse;
}
protected static int CreateCreditCardRecord(CreditCard newCC, Profile currentProfile, CreditCardTransactionResponse cctResponse, string status, string transactionType)
{
CreditCardRecord newCCRecord = new CreditCardRecord();
newCCRecord.Address = newCC.Address;
newCCRecord.AddressVerificationLevel = newCC.AVLevel;
newCCRecord.Amount = newCC.Amount;
newCCRecord.CardCode = newCC.CardCode;
newCCRecord.CardNumber = newCC.CardNumber;
newCCRecord.CCGatewayID = cctResponse.CreditCardTransactionID;
newCCRecord.City = newCC.City;
newCCRecord.CompanyName = newCC.CompanyName;
newCCRecord.CreateBy = currentProfile.ACHCompanyName;
newCCRecord.CreateOn = DateTime.Now;
newCCRecord.Description = newCC.Description;
newCCRecord.Expiration = newCC.Expiration;
newCCRecord.FirstName = newCC.FirstName;
newCCRecord.LastName = newCC.LastName;
newCCRecord.Profile.ProfileID = currentProfile.ProfileID;
newCCRecord.State = newCC.State;
newCCRecord.Status = status;
newCCRecord.TransactionType = transactionType;
newCCRecord.Zip = newCC.Zip;
return CreditCardRecordsGateWay.Insert(newCCRecord);
}
You have just posted the constructors to your SFBExternalPaymentsEntities class, but you seem to say that you get an exception when you call a method that returns a collection of entities, which makes send with the exception message you are getting. The chances are the problem lies within the method you are calling, or in the code calling it rather than in the constructor code you have posted. Can you post some more relevent code or explain how the code you have posted relates to the exception.
I encountered the same problem when i updated one of my projects from .net 4.0 to 4.5, the reason probably was because there was another .edmx file in an other project that was referenced in the target project.
Solution to issue : Updated the other project that was referenced and contained .edmx file to .net 4.5 and then, Right-Clicked on every .edmx files in these 2 projects and then selected 'Run Custom Tool'
It worked for me, hope that it works for everyone reading this article

Get order information such as inventory id, customer id and location id in a sales order during Business Logic Customization

As I mentioned in my previous question( How to customize the sales order process to trigger an automatic "adding contract" process when sales order is successfully completed), I need to automatically add a contract for each of some particular products that are in a sales order after this sales order is added.
I have learned adding contract part in previous questions,thanks to #Gabriel's response, and now I need to know how to get those order information such as inventory id in order items, customer id and location id in a sales order business logic (screen SO301000). Would anybody please kindly provide me some sample code?
Thanks.
Now I seem to be able to get customer id and location id from code:
SOOrder SalesOrder = (SOOrder)Base.Caches[typeof(SOOrder)].Current;
int customer_id = SalesOrder.CustomerID;
int Location ID = SalesOrder.CustomerLocationID;
....
but I still need to find out how to iterate through product list (SOLine item) in the order...the code I found as below (it was an example for implementing a SO release operation) in T200 training PDF seems too old and not helpful to me:
public static void ReleaseOrder(SalesOrder order)
{
SalesOrderEntry graph = PXGraph.CreateInstance<SalesOrderEntry>();
graph.Orders.Current = order;
foreach (OrderLine line in graph.OrderDetails.Select())
{
ProductQty productQty = new ProductQty();
productQty.ProductID = line.ProductID;
productQty.AvailQty = -line.OrderQty;
graph.Stock.Insert(productQty);
}
order.ShippedDate = graph.Accessinfo.BusinessDate;
order.Status = OrderStatus.Completed;
graph.Orders.Update(order);
graph.Persist();
}
This is how you have to loop through the lines while you override persist in your extension
foreach (SOLine line in this.Base.Transactions.Select())
{
}
What you are doing here is you are looping through the valid records in the cache by executing select method. For this you have to find the view definition (Transactions) related to the DAC(SOLine) from the Base BLC definitions.
I figured out how to do it and the below is the code what I have and it's working for me so far - you can see I use "SOLine_RowPersisted" instead of customizing "Persist()" as I did before.
protected virtual void SOLine_RowPersisted(PXCache sender,PXRowPersistedEventArgs e)
{
if (e.TranStatus == PXTranStatus.Completed)
{
if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Insert)
{
SOOrder SalesOrder = (SOOrder)Base.Caches[typeof(SOOrder)].Current;
SOLine line = (SOLine)e.Row;
// Lookup inventory
InventoryItem template = PXSelect<InventoryItem,
Where<InventoryItem.inventoryID, Equal<Required<InventoryItem.inventoryID>>>>
.Select(Base, line.InventoryID);
if (template.InventoryCD == null)
{
throw new PXException("Inventory CD can not be blank.");
}
if (template.InventoryCD.StartsWith("AAABBB"))
{
ContractMaint contractMaint = PXGraph.CreateInstance<ContractMaint>();
CTBillEngine engine = PXGraph.CreateInstance<CTBillEngine>();
DateTime StartDate = DateTime.Now;
........
string contractCD = ......
Contract contract = SetupActivateContract(contractMaint, contractCD, StartDate , line.CustomerID, SalesOrder.CustomerLocationID, engine);
}
} else if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Delete)
{
.....
} else if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Update)
{
....
}
}
}

InstanceContextMode.PerSession not working

I m using InstanceContextMode.PerSession in wshttpbinding as mentioned in below code but it seems not working,since my count is not increasing.
Please advice.
[ServiceContract(SessionMode = SessionMode.Required)]
public interface ITransService
{
[OperationContract(IsInitiating=true)]
int Insert(int userId);
[TransactionFlow(TransactionFlowOption.Allowed)]
[OperationContract]
int Update();
// TODO: Add your service operations here
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class TransService : ITransService
{
public int count = 0;
[OperationBehavior(TransactionScopeRequired = true)]
public int Insert(int userId)
{
count = count+1;
return count;
}
[OperationBehavior(TransactionScopeRequired = true)]
public int Update()
{
count = count++;
return count;
}
}
Client Call ---
using (TransactionScope tranScope = new TransactionScope())
{
TransServiceClient obj = new TransServiceClient();
var a= obj.Insert(123);
var b = obj.Insert(123);
var c = obj.Update();
tranScope.Complete();
}
In addition to marking the interface as (SessionMode = SessionMode.Allowed), you'll also need to define which methods on the interface initiate the session, like so: e.g. if Just Insert initiates the session: [OperationContract(IsInitiating=true)] int Insert(int userId); There's an example here
(No, IsInitiating = true is the default in any event)
As per the extended commentary, a trial and error approach eventually yielded that TransactionFlow prevents SessionMode from working correctly. I cannot currently find a definitive reference on this - the closest to date is this. Logically, sessions can last for far longer durations than Transactions (and given that Transactions could hold locks on database and queue resources) there is some logic.
The solution was to remove TransactionFlow.
Edit
For those looking to combine both InstanceContextMode = InstanceContextMode.PerSession and TransactionFlow, see here

Entity Framework code. Efficiency Suggestions

I am a newbie learning entity framework ( VS2012) and writing a simple CRUD application for testing. I have created the following function for a simple insert / update. I want to know if its ok or have any flaws and can be improved?
This function will be in a class library class file and will be called from the web UI on form submission.
Here is the function :
public static bool Save(int id, string hospitalname, string hospitaladdress, int cityid,
string postcode, int countryid, string email, string phone, string fax, string contactperson,
string otherdetails, bool isactive, DateTime createddate)
{
bool flag = false;
using (var dataContext = new pacsEntities())
{
if (id == 0)
{
// insert
var newhospital = new hospital_master();
newhospital.hospitalname = hospitalname;
newhospital.hospitaladdress = hospitaladdress;
newhospital.cityid = cityid;
newhospital.postcode = postcode;
newhospital.countryid = countryid;
newhospital.email = email;
newhospital.phone = phone;
newhospital.fax = fax;
newhospital.contactperson = contactperson;
newhospital.otherdetails = otherdetails;
newhospital.isactive = isactive;
newhospital.createddate = DateTime.Now;
dataContext.hospital_master.AddObject(newhospital);
dataContext.SaveChanges();
flag = true;
}
else
{
// update
var hospital = dataContext.hospital_master.First(c => c.hospitalid == id);
if (hospital != null)
{
hospital.hospitalname = hospitalname;
hospital.hospitaladdress = hospitaladdress;
hospital.cityid = cityid;
hospital.postcode = postcode;
hospital.countryid = countryid;
hospital.email = email;
hospital.phone = phone;
hospital.fax = fax;
hospital.contactperson = contactperson;
hospital.otherdetails = otherdetails;
hospital.isactive = isactive;
dataContext.SaveChanges();
flag = true;
}
}
}
return flag;
}
There are multiple improvements you could do:
1- You could create a class Hospital and pass it
2- I personally think that throwing an exception is better than returning flag to determine failure, but still can return true on success
3- save context can be written once outside the if
4- .First in the update scenario can be FirstOrDefault, and check for null if you want to return flag for success/failure; or just let First fail in case you want to throw exceptions as opposed to returning true/false, but one way or the other, be consistent, you either throw exceptions or return true/false. You are using First and checking for null!
5- It is good to have your own model classes (Hospital) different than your entity classes (hospital_master), and in your higher layers you use your Hospital class and in your data layer, where you have your Save method, you convert, or Hydrate/dehydrate your objects.
6 - You could simplify creation of object by:
new hospital_master
{
hospitalname = hospital.hospitalname,
hospitaladdress = hospital.hospitaladdress,
...
};
7- You could have extension methods on your Hospital class (ToEntity) and simply call it to save:
dataContext.hospiltal_master.AddObject(hospital.ToEntity());
8- It seems the EntitySet (set of hospital_master containing instances of hospital_masters) has the same name as the entity itself. You are creating hospital_master entity, and then adding it to hospital_master: datacontext.hospital_master.AddObject... I would think there should be datacontext.hospital_masters.AddObect... plural.
You may implement a generic repository that will be responsible for all CRUD operations.
For example:
http://geekswithblogs.net/seanfao/archive/2009/12/03/136680.aspx
You will be able to use it for all entities that you have in your application.

Resources