I have 2 custom objects: Game__c and Bet__c. I need to create trigger in which the system should calculate Game Rating based on the Total Money Bet (more money means higher rating).
TotalMoneyBet__c - its Roll-up Summay filed on Game__c
Every time a new bet is created, recalculate and set the rating: max totalmoneybet = gamerating 1
I've write a code and its working but not include triggered bet. Can you help me with this issue?
public class CalculateGameRatingTriggerHandler {
public static void afterInsert(List<Bet__c> triggeredBet) {
List<Game__c> topGames = [SELECT Id, TotalMoneyBet__c, GameRating__c
FROM Game__c
ORDER BY TotalMoneyBet__c DESC LIMIT 10];
for(Integer i = 0; i < 10; i++) {
for(Game__c game : topGames) {
topGames[i].GameRating__c = i+1;
}
}
update topGames;
}
}
trigger BetTriggerSecond on Bet__c (after insert) {
CalculateGameRatingTriggerHandler.afterInsert(trigger.new);
}
Related
public static void updatecasefields(List<Case> lstcase) {
//List<Case> lstcase = new list<case>();
ID devRecordTypeId = Schema.SObjectType.Case.getRecordTypeInfosByDeveloperName().get('CRM_CSR_Case').getRecordTypeId();
for (Case cs: lstcase) {
if(cs.ID != null && cs.RecordTypeId == devRecordTypeId) {
}
List<CRM_CasePick__c> Casp = [SELECT Id, CRM_Carrier_Name__c,CRM_LOB__c, CRM_SLA_Turnaround_Time__c,CRM_Category__c, CRM_Issue_Sub_Type__c,CRM_Issue_Type__c,CRM_Turnaround_Time_Days__c FROM CRM_CasePick__c WHERE CRM_Carrier_Name__c = :cs.GiDP_CarrierName__c AND CRM_Category__c = :cs.CRM_Category__c AND CRM_Issue_Type__c = :cs.CRM_Issue_Type__c AND CRM_Issue_Sub_Type__c = :cs.CRM_Issue_Sub_Type__c AND CRM_LOB__c = :cs.CRM_Line_of_Business__c];
for(CRM_CasePick__c CP: Casp) {
cs.CRM_Turnaround_Time_Days__c = cp.CRM_Turnaround_Time_Days__c;
cs.CRM_SLA_Turnaround_time__c = cp.CRM_SLA_Turnaround_Time__c;
}
}
}
Remove the SOQL query from the for loop - best practice is to never run a query within a loop.
Right now it is running that query for every value of the initial list. If the list is over 100 records, it will exceed the governor limit.
I recently started using Unity3D and made a few levels. The only problem I have right now is how can I get a live counter?
So my character dies when he hits and certain object.
I want my character to get 3 lives maximum, and get -1 live when he hits that object.
And that it keeps the data when he dies so you wouldn't get lives back if you restart the app.
And after a certain amount of minutes he gets +1 live.
Thank you :)
While your game running. just create a variable counterTime to count time, whenever counterTime pass certain amount of time you want reset counterTime to 0 and increase your life.
When user quit your app, save last time to PlayerPref, eg:
PlayerPref.SaveString("LastTime", DateTime.Now);
When user comback game, just check duration between last time and now to calculate total life need added. eg:
DateTime lastTime = DateTime.Parse(PlayerPref.GetString("LastTime"));
TimeSpan timeDif= DateTime.Now - lastTime;
int duration = timeDif.TotalSeconds;
You can use PlayerPrefs.SetInt , PlayerPrefs.GetInt for storing and reading your player's hp in file storage. Read more about it here:
https://docs.unity3d.com/ScriptReference/PlayerPrefs.html
As for Giving player +1 hp after a few minutes you can store DateTime.Now in a PlayerPrefs variable whenever you give your player some hp and use TimeSpan and TotalMinutesPassed:
TimeSpan passedTime = DateTime.Now - lastStoredDateTime;
int totalMinutesPassed = passedTime.TotalMinutes;
Should go sth like this i guess(didnt test this code just showing a general idea) :
void SetPlayerLives(int lives)
{
playerLives = lives;
PlayerPrefs.SetInt("player-lives",playerLives);
}
//TODO: also sth like => int GetPlayerLives() function
void CheckLiveRegen() //call this function whenever you want to check live regen:
{
int LIVE_REGEN_MINUTES = 5; //regen 1 live every 5 minutes
DateTime lastStoredDateTime = DateTime.Parse(PlayerPrefs.GetString("last-live-regen", DateTime.Now.ToString()));
TimeSpan passedTime = DateTime.Now - lastStoredDateTime;
double totalMinutesPassed = passedTime.TotalMinutes;
if(totalMinutesPassed >= LIVE_REGEN_MINUTES)
{
int val = (int) totalMinutesPassed / LIVE_REGEN_MINUTES;
// Add val to your player lives! + store new lives value
SetPlayerLives(playerLives+val);
//update last-live-regen value:
PlayerPrefs.SetString("last-live-regen", DateTime.Now.ToString());
}
}
Note: DateTime , TimeSpan classes have some bugs (specially in android platform) in versions older than 2017.4 (LTS) Make sure you log values and check if functions are working properly.
https://forum.unity.com/threads/android-datetime-now-is-wrong.488380/
check out the following link to understand how to create a life counter in unity
http://codesaying.com/life-counter-in-unity/
In order to calculate the time that was lapsed since you last shut down the game, you should save the last time playerprefs in the function OnApplicationPause and calcuate the timelapsed in the Awake Function.
void Awake () {
if(!PlayerPrefs.HasKey("Lives")){
PlayerPrefs.SetString("LifeUpdateTime", DateTime.Now.ToString());
}
lives = PlayerPrefs.GetInt("Lives", maxLives);
//update life counter only if lives are less than maxLives
if (lives < maxLives)
{
float timerToAdd = (float)(System.DateTime.Now - Convert.ToDateTime(PlayerPrefs.GetString("LifeUpdateTime"))).TotalSeconds;
UpdateLives(timerToAdd);
}
}
void UpdateLives(double timerToAdd ){
if (lives < maxLives)
{
int livesToAdd = Mathf.FloorToInt((float)timerToAdd / lifeReplenishTime);
timerForLife = (float)timerToAdd % lifeReplenishTime;
lives += livesToAdd;
if (lives > maxLives)
{
lives = maxLives;
timerForLife = 0;
}
PlayerPrefs.SetString("LifeUpdateTime", DateTime.Now.AddSeconds(-timerForLife).ToString());
}else{
PlayerPrefs.SetString("LifeUpdateTime", DateTime.Now.ToString());
}
}
void OnApplicationPause(bool isPause)
{
if (isPause)
{
timeOfPause = System.DateTime.Now;
}
else
{
if(timeOfPause == default(DateTime)){
timeOfPause = System.DateTime.Now;
}
float timerToAdd = (float)(System.DateTime.Now - timeOfPause).TotalSeconds;
timerForLife += timerToAdd;
UpdateLives(timerForLife);
}
}
}
When work order updates to Completed then a new water meter reading record needs to created with two field values from work order and two from another object att. Both work order and water meter reading have a look up(workorder) and master detail relation(water meter reading) with att.Sorry for so much code,but I am really stuck and need help.
trigger CreateWaterMeterReading on sm1e__smWork_Order__c (after update)
{
if (Trigger.new.size() == 1)
{
sm1e__smWork_Order__c wo = Trigger.new[0];
if(wo.sm1e__WO_Type__c == 'Meter Read Move In/Out ' && wo.sm1e__Status__c == 'Completed')
reateNewWaterMeterRead.createWMRforMoveInOrOut(wo.Id);
}
}
--Apex class
public class CreateNewWaterMeterRead {
public static void createWMRforMoveInOrOut(string workorderId)
{
Work_Order__c wo = [Select Equipment__r.Name,Completion_Date__c,Meter_Reading__c from Work_Order__c where Id = : workorderId ];
Equipment__c att = [Select Id,Last_Water_Meter_Reading_Date__c,Last_Water_Meter_Reading__c from Equipment__c where Name = : wo.Equipment__r.Name ];
List<Water_Meter_Readings__c> newwmr = new List<Water_Meter_Readings__c>();
Water_Meter_Readings__c wmr = new Water_Meter_Readings__c();
wmr.Meter__c = att.Id;
wmr.Current_Meter_Reading__c = wo.Meter_Reading__c;
wmr.Current_Read_Date__c = wo.sm1e__Completion_Date__c;
wmr.Prior_Meter_Reading__c = att.Last_Water_Meter_Reading__c;
wmr.Prior_Read_Date__c = att.Last_Water_Meter_Reading_Date__c;
wmr.Source__c = 'Manual Read';
newwmr.add(wmr);
if(newwmr.size() >0)
insert newwmr;
}
--Test Class
isTest(SeeAllData = true)
public class CreateNewWaterMeterReadTest
{
static testmethod void createWMRforMoveInOrOut()
{
Work_Order__c wo = [Select Id,Equipment__r.Name,Completion_Date__c,Meter_Reading__c from Work_Order__c where sm1e__Status__c != 'Completed' AND sm1e__WO_Type__c = 'Meter Read Move In/Out' LIMIT 1];
Equipment__c att = [Select Id,Last_Water_Meter_Reading_Date__c,Last_Water_Meter_Reading__c from Equipment__c where Name = : wo.Equipment__r.Name ];
test.startTest();
wo.Meter_Reading__c = 1317;
wo.sm1e__Status__c = 'Completed';
update wo;
test.stopTest();
System.debug('updated wo');
Water_Meter_Readings__c wmr = new Water_Meter_Readings__c();
System.debug('wmr for test');
wmr.Meter__c= att.Id;
wmr.Current_Meter_Reading__c = wo.Meter_Reading__c;
wmr.Current_Read_Date__c = wo.Completion_Date__c;
System.debug('in between wmr');
wmr.Prior_Meter_Reading__c = att.Last_Water_Meter_Reading__c;
wmr.Prior_Read_Date__c = att.Last_Water_Meter_Reading_Date__c;
wmr.Source__c = 'Manual';
insert wmr;
You've written trigger on after update. But in your test class you're inserting a record and not updating it. That is why your code coverage is 0%. Refer to this link
For executing trigger you must do dml operation for which you've written a trigger. Also you can invoke your class method from test class by creating its instance. Refer above link for the same.
I'm trying to create trigger if record type is Revenue Risk then amount should be saved in negative value, Here's my code in which I'm having error, I tried it two ways, second is in comments.. none of them is working
public with sharing class amountValidator {
//pull data of Opportunity in list
public static void validateAmount (list<Opportunity> oppList){
oppList = [Select amount FROM Opportunity WHERE RecordType.Name IN ('Revenue Risk')];
for(Opportunity opportunities : oppList){
if(oppList.amount >= '0'){
oppList.amount = oppList.amount * '-1';
}
}
/*Map<String,Schema.RecordTypeInfo> rtMapByName = d.getRecordTypeInfosByName();
Schema.RecordTypeInfo rtByName = rtMapByName.get('Revenue Risk');
for(Opportunity each : oppList){
if(rtByName.size == 0){
}
else{
if(oppList.Amount >= 0){
oppList.Amount = oppList.Amount*-1;
}
}
}*/
The error is very clear:
if(oppList.amount >= '0'){ // THIS LINE WILL THROW AN ERROR: 'Comparison arguments must be compatible types: Integer (or Double), String
oppList.amount = oppList.amount * '-1'; // THIS ONE TOO: 'Arithmetic expressions must use numeric arguments'
}
Your second code snippet is also wrong (same for first one).
if(oppList.Amount >= 0){
oppList.Amount = oppList.Amount*-1;
// MUST BE each.Amount = each.Amount * - 1; Please try not to use reserved words as variable names
}
You may want to take a look at a previous post describing strongly typed programming languages: Strongly Typed
Since we can't add comments just yet, we're going to add a new answer:
You're not updating/inserting the updated amount for your opportunity.
The correct way of doing this is to create a separate List of Opportunities (i.e. List oppsToUpdate) and add the updated opportunities to this list.
public static void validateAmount (list<Opportunity> oppList){
oppList = [Select amount FROM Opportunity WHERE RecordType.Name IN ('Revenue Risk')]; // Why are you requerying the Opportunity if you already have it??
List<Opportunity> oppsToUpdate = new List<Opportunity>();
for(Opportunity opportunities : oppList){
if(opportunities.amount > 0){
opportunities.amount = opportunities.amount * -1;
oppsToUpdate.add(opportunities);
}
}
upsert opportunities;
}
Remember to enclose your function with try-catch statements with system debugs to see what's going on with your code.
And this is the link to the input parameter modifications and why this is bad practice: Input Parameters
Working Code:
trigger Risk_NegativeQuantity on OpportunityLineItem (before insert) {
set<id> oppid = new set<id>();
for (OpportunityLineItem oli : trigger.new)
{
oppid.add(oli.opportunityid);
}
Id RevenueRisk= Schema.SObjectType.Opportunity.getRecordTypeInfosByName().get('Revenue Risk').getRecordTypeId();
list<opportunity> opplist = [select id, recordtype.name,recordtypeid from opportunity where id in : oppid ];
for (OpportunityLineItem oli : trigger.new)
{
for (opportunity opp: opplist)
{
if (oli.opportunityid == opp.id)
{
if(opp.recordtype.name == 'Revenue Risk')
{
if(oli.Quantity > 0)
{
oli.Quantity = oli.Quantity * -1;
}
}
}
}
}
}
please help me in converting my after trigger to batch apex.
This trigger fires when opportunity stage changes to won.
It runs through line items and checks if forecast(custom objet) exists with that acunt.if yes,iit links to them..if no,itt will create a new forecat.
my trigger works fine forr some records.but to mass update i am getting timed out error.So opting batch apex but i had never written it.pls help me.
trigger Accountforecast on Opportunity (after insert,after update) {
List<Acc_c> AccproductList =new List<Acc_c>();
List<Opportunitylineitem> opplinitemlist =new List<Opportunitylineitem>();
list<opportunitylineitem > oppdate= new list<opportunitylineitem >();
List<Acc__c> accquery =new List<Acc__c>();
List<date> dt =new List<date>();
Set<Id> sProductIds = new Set<Id>();
Set<Id> sAccountIds = new Set<Id>();
Set<id> saccprodfcstids =new set<Id>();
Acc__c accpro =new Acc__c();
string aname;
Integer i;
Integer myIntMonth;
Integer myIntyear;
Integer myIntdate;
opplinitemlist=[select Id,PricebookEntry.Product2.Name,opp_account__c,Opp_account_name__c,PricebookEntry.Product2.id, quantity,ServiceDate,Acc_Product_Fcst__c from Opportunitylineitem WHERE Opportunityid IN :Trigger.newMap.keySet() AND Acc__c=''];
for(OpportunityLineItem oli:opplinitemlist) {
sProductIds.add(oli.PricebookEntry.Product2.id);
sAccountIds.add(oli.opp_account__c);
}
accquery=[select id,Total_Qty_Ordered__c,Last_Order_Qty__c,Last_Order_Date__c,Fcst_Days_Period__c from Acc__c where Acc__c.product__c In :sproductids and Acc__c.Account__c in :saccountids];
for(Acc__c apf1 :accquery){
saccprodfcstids.add(apf1.id);
}
if(saccprodfcstids!=null){
oppdate=[select servicedate from opportunitylineitem where Acc__c IN :saccprodfcstids ];
i =[select count() from Opportunitylineitem where acc_product_fcst__c in :saccprodfcstids];
}
for(Opportunity opp :trigger.new)
{
if(opp.Stagename=='Closed Won')
{
for(opportunitylineitem opplist:opplinitemlist)
{
if(!accquery.isempty())
{
for(opportunitylineitem opldt :oppdate)
{
string myDate = String.valueOf(opldt);
myDate = myDate.substring(myDate.indexof('ServiceDate=')+12);
myDate = myDate.substring(0,10);
String[] strDate = myDate.split('-');
myIntMonth = integer.valueOf(strDate[1]);
myIntYear = integer.valueOf(strDate[0]);
myIntDate = integer.valueOf(strDate[2]);
Date d = Date.newInstance(myIntYear, myIntMonth, myIntDate);
dt.add(d);
}
dt.add(opp.closedate);
dt.sort();
integer TDays=0;
system.debug('*************dt:'+dt.size());
for(integer c=0;c<dt.size()-1;c++)
{
TDays=TDays+dt[c].daysBetween(dt[c+1]);
}
for(Acc_product_fcst__c apf:accquery)
{
apf.Fcst_Days_Period__c = TDays/i;
apf.Total_Qty_Ordered__c =apf.Total_Qty_Ordered__c +opplist.quantity;
apf.Last_Order_Qty__c=opplist.quantity;
apf.Last_Order_Date__c=opp.CloseDate ;
apf.Fcst_Qty_Avg__c=apf.Total_Qty_Ordered__c/(i+1);
Opplist.Acc__c =apf.Id;
}
}
else{
accpro.Account__c=opplist.opp_account__c;
accpro.product__c=opplist.PricebookEntry.Product2.Id;
accpro.opplineitemid__c=opplist.id;
accpro.Total_Qty_Ordered__c =opplist.quantity;
accpro.Last_Order_Qty__c=opplist.quantity;
accpro.Last_Order_Date__c=opp.CloseDate;
accpro.Fcst_Qty_Avg__c=opplist.quantity;
accpro.Fcst_Days_Period__c=7;
accproductList.add(accpro);
}
}
}
}
if(!accproductlist.isempty()){
insert accproductlist;
}
update opplinitemlist;
update accquery;
}
First of all, you should take a look at this: Apex Batch Processing
Once you get a better idea on how batches work, we need to take into account the following points:
Identify the object that requires more processing. Account? Opportunity?
Should the data be maintained across batch calls? Stateful?
Use correct data structure in terms of performance. Map, List?
From your code, we can see you have three objects: OpportunityLineItems, Accounts, and Opportunities. It seems that your account object is using the most processing here.
It seems you're just keeping track of dates and not doing any aggregations. Thus, you don't need to maintain state across batch calls.
Your code has a potential of hitting governor limits, especially memory limits on the heap. You have a four-nested loop. Our suggestion would be to maintain opportunity line items related to Opportunities in a Map rather than in a List. Plus, we can get rid of those unnecessary for loops by refactoring the code as follows:
Note: This is just a template for the batch you will need to construct.
globalglobal Database.QueryLocator start(Database.BatchableContext BC) class AccountforecastBatch implements Database.Batchable<sObject>
{
global Database.QueryLocator start(Database.BatchableContext BC)
{
// 1. Do some initialization here: (i.e. for(OpportunityLineItem oli:opplinitemlist) {sProductIds.add(oli.PricebookEntry.Product2.id)..}
// 2. return Opportunity object here: return Database.getQueryLocator([select id,Total_Qty_Ordered__c,Last_Order_Qty ....]);
}
global void execute(Database.BatchableContext BC, List<sObject> scope)
{
// 1. Traverse your scope which at this point will be a list of Accounts
// 2. You're adding dates inside the process for Opportunity Line Items. See if you can isolate this process outside the for loops with a Map data structure.
// 3. You have 3 potential database transactions here (insert accproductlist;update opplinitemlist; update accquery; ). Ideally, you will only need one DB transaction per batch.If you can complete step 2 above, you might only need to update your opportunity line items. Otherwise, you're trying to do more than one thing in a method and you will need to redesign your solution
}
global void finish(Database.BatchableContext BC)
{
// send email or do some other tasks here
}
}