In general it's good to write some equations before. But when you rely on an algebraic modeling language like OPL you may also directly try your ideas.
Disclosure: I am the author of the linked article.
Let me share a starting point with OPL CPLEX
execute
{
cplex.optimalitytarget=3;
}
range weeks=1..100;
tuple need
{
int startweek;
int endweek;
int quantity;
}
{need} needs={<1,5,20>,<6,20,40>,<21,40,35>,
<41,60,55>,<61,80,75>,<81,100,60>};
int demand[w in weeks]=
first({n | n in needs : n.startweek<=w && w<=n.endweek}).quantity;
int trainingTime=4;
float overtime=0.2;
float undertime=-0.1;
float overtimecost=1.5;
float cost=10;
float hiringcost=2000;
int startEmployees=20;
int nbhours=8;
dvar int+ hire[weeks];
dvar int+ nbEmployees[weeks];
dvar float underover[weeks];
dexpr float hiringCost=hiringcost*sum(w in weeks) hire[w];
dexpr float salaryCost=nbhours*cost*sum(w in weeks)nbEmployees[w];
dexpr float underovertimeCost=nbhours*cost*
sum(w in weeks) (overtimecost*(underover[w]>=0)*underover[w]+
(underover[w]<=0)*underover[w]);
minimize hiringCost+salaryCost+underovertimeCost;
subject to
{
// stating point
forall(i in 1..trainingTime)nbEmployees[i]==startEmployees;
// satisfy demand
forall(w in weeks)
{
nbEmployees[w]+underover[w]>=demand[w];
nbEmployees[w]+underover[w]<=demand[w];
}
// undertime and overtime
forall(w in weeks)
{
underover[w]<=overtime*nbEmployees[w];
underover[w]>=undertime*nbEmployees[w];
}
// hiring employees
forall(i in weeks:(i+trainingTime) in weeks )
nbEmployees[i+trainingTime]==nbEmployees[i+trainingTime-1]+hire[i];
}