#include <bits/stdc++.h>
using namespace std;
class Expense{
public:
string expense_id;
string trip_id;
string amount_usd;
string expense_type;
string vendor_type;
string vendor_name;
};
class Violation{
public :
string expense_id;
string message;
};
class Rule{
public:
virtual bool violates(Expense e)=0;
virtual string description()=0;
virtual void finalize(vector<Violation> &violations){};
};
class NoFieldRule : public Rule {
public :
string fieldName;
string fieldValue;
NoFieldRule(string fieldName,string fieldValue){
this->fieldName = fieldName;
this->fieldValue = fieldValue;
}
string getField(Expense e, string fieldName){
if(fieldName=="expense_id") return e.expense_id;
if(fieldName=="trip_id") return e.trip_id;
if(fieldName=="amount_usd") return e.amount_usd;
if(fieldName=="expense_type") return e.expense_type;
if(fieldName=="vendor_type") return e.vendor_type;
if(fieldName=="vendor_name") return e.vendor_name;
return "";
}
bool violates(Expense e){
return getField(e,fieldName)==fieldValue;
}
string description() override {
return "No "+ fieldValue + " "+ fieldName +" expense allowed";
}
};
class FieldLimitRule : public Rule {
public :
string fieldName;
string limit;
string filterFieldName;
string filterFieldValue;
FieldLimitRule(string fieldName,string limit,string filterFieldName="",string filterFieldValue=""){
this->fieldName = fieldName;
this->limit = limit;
this->filterFieldName= filterFieldName;
this->filterFieldValue = filterFieldValue;
}
string getField(Expense e, string fieldName){
if(fieldName=="expense_id") return e.expense_id;
if(fieldName=="trip_id") return e.trip_id;
if(fieldName=="amount_usd") return e.amount_usd;
if(fieldName=="expense_type") return e.expense_type;
if(fieldName=="vendor_type") return e.vendor_type;
if(fieldName=="vendor_name") return e.vendor_name;
return "";
}
bool violates(Expense e){
if(filterFieldName.empty())
return stod(getField(e,fieldName))>stod(limit);
else{
string filterFieldValue = getField(e,filterFieldName);
return filterFieldValue == this->filterFieldValue && stod(getField(e,fieldName)) > stod(limit);
}
}
string description() override {
if(!filterFieldName.empty()){
return "No "+ filterFieldValue + " "+ fieldName +" expense can exceed "+ limit +"for " + fieldName;
}
else{
return "No expense can exceed "+ limit +"for " + fieldName;
}
}
};
class AggregatedRule : public Rule {
public :
string fieldName;
string limit;
string filterFieldName;
string filterFieldValue;
string groupField;
unordered_map<string,double> totals;
AggregatedRule(string groupField,string fieldName,string limit,string filterFieldName="",string filterFieldValue=""){
this->groupField = groupField;
this->fieldName = fieldName;
this->limit = limit;
this->filterFieldName= filterFieldName;
this->filterFieldValue = filterFieldValue;
}
string getField(Expense e, string fieldName){
if(fieldName=="expense_id") return e.expense_id;
if(fieldName=="trip_id") return e.trip_id;
if(fieldName=="amount_usd") return e.amount_usd;
if(fieldName=="expense_type") return e.expense_type;
if(fieldName=="vendor_type") return e.vendor_type;
if(fieldName=="vendor_name") return e.vendor_name;
return "";
}
bool violates(Expense e){
if(!filterFieldName.empty() && getField(e,filterFieldName)!=filterFieldValue){
return false;
}
string value = getField(e,groupField);
totals[value]+=stod(getField(e,fieldName));
return false;
}
void finalize(vector<Violation> &violations){
for(auto it : totals){
string key = it.first;
double value = it.second;
string message = "";
if(value>stod(limit)){
if(!filterFieldName.empty()){
message += key + " violates "+ filterFieldValue +" "+limit+".";
}
else{
message += key + "violates " + limit+".";
}
violations.push_back({key,message});
}
}
}
string description() override {
if(!filterFieldName.empty()){
return "No "+ filterFieldValue + " "+ fieldName +" expense can exceed "+ limit +"for " + fieldName;
}
else{
return "No expense can exceed "+ limit +"for " + fieldName;
}
}
};
vector<Violation> evaluate(vector<Rule*> rules, vector<Expense> expenses){
vector<Violation> violations;
for(auto expense: expenses){
for(auto rule : rules){
if(rule->violates(expense)){
violations.push_back({expense.expense_id,rule->description()});
}
}
}
for(auto rule: rules){
rule->finalize(violations);
}
return violations;
}
int main(){
vector<Expense> expenses = {
{"e1","t1","50","meal","restaurant","Dominoes"},
{"e2","t1","25","meal","restaurant","Sapphire"},
{"e3","t2","200","airfare","transportation","Virgin_Atlantic"},
{"e4","t2","120","entertainment","cinema","PVR"},
{"e5","t2","58","client_hosting","restaurant","Saphire"},
{"e6","t3","91","client_hosting","restaurant","Saphire"},
{"e7","t1","2200","lodging","hotel","Marriot"}
};
FieldLimitRule restaurantLimitRule("amount_usd","75","vendor_type","restaurant");
NoFieldRule airfareRule("expense_type","airfare");
NoFieldRule entertainmentRule("expense_type","entertainment");
FieldLimitRule expenseLimitRule("amount_usd","2000");
AggregatedRule tripExpenseRule("trip_id","amount_usd","2000");
AggregatedRule tripMealRule("trip_id","amount_usd","200","expense_type","meal");
vector<Rule*> rules = {&restaurantLimitRule,&airfareRule,&entertainmentRule,&expenseLimitRule,&tripExpenseRule,&tripMealRule};
vector<Violation> violations = evaluate(rules,expenses);
for(auto violation : violations){
cout<<"Id: "<<violation.expense_id<<" - "<<violation.message<<endl;
}
return 0;
}