Ver 0.1
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.Rproj.user
|
||||
.Rhistory
|
||||
.RData
|
||||
.Ruserdata
|
||||
212
Pre_runner.Rmd
Normal file
212
Pre_runner.Rmd
Normal file
@@ -0,0 +1,212 @@
|
||||
---
|
||||
title: "Pre"
|
||||
author: "Scary Scarecrow"
|
||||
date: '2022-06-27'
|
||||
output: html_document
|
||||
---
|
||||
|
||||
```{r setup, include=FALSE}
|
||||
knitr::opts_chunk$set(echo = TRUE)
|
||||
library(dplyr)
|
||||
library(echarts4r)
|
||||
library(lubridate)
|
||||
library(shinymanager)
|
||||
```
|
||||
|
||||
## Credentials
|
||||
|
||||
```{r}
|
||||
credentials <- data.frame(
|
||||
user = c("shiny", "asitav", "rigo", "aldo"),
|
||||
password = c("lanubia@2021", "lanubia@2021","lanubia@2021","lanubia@2021"),
|
||||
admin = c(FALSE, TRUE, FALSE, FALSE),
|
||||
email = c("hello@asitavsen.com","asitav.sen@lanubia.com","rigo.selassa@lanubia.com","aldo.silvano@lanubia.com"),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
|
||||
create_db(
|
||||
credentials_data = credentials,
|
||||
sqlite_path = "./cred.sqlite", # will be created
|
||||
passphrase = "kJuyhG657Hj&^%gshj*762hjsknh&662"
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## DB
|
||||
|
||||
```{r}
|
||||
connec <- dbConnect(
|
||||
RPostgres::Postgres(),
|
||||
dbname = dsn_database,
|
||||
host = dsn_hostname,
|
||||
port = dsn_port,
|
||||
user = dsn_uid,
|
||||
password = dsn_pwd
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
||||
```{r}
|
||||
#dat<-read.csv("./data/initial.csv")
|
||||
#lims<-read.csv("./data/dev.limits.csv")
|
||||
dat.1<-
|
||||
dat |>
|
||||
mutate(devia=Actual-Plan) |>
|
||||
mutate(devia.per=round(devia/Plan,2))
|
||||
#dbCreateTable(connec, "calculated", dat.1)
|
||||
dbWriteTable(connec, "calculated", dat.1, append=TRUE)
|
||||
dbWriteTable(connec, "limits", lims, append=TRUE)
|
||||
|
||||
#dbRemoveTable(connec,"calculated")
|
||||
dat<-dbGetQuery(
|
||||
connec,
|
||||
'SELECT * FROM calculated'
|
||||
)
|
||||
|
||||
lims<-dbGetQuery(
|
||||
connec,
|
||||
'SELECT * FROM limits'
|
||||
)
|
||||
|
||||
#dbReadTable(connec, "calculated")
|
||||
|
||||
exp<-
|
||||
dat |>
|
||||
inner_join(lims, by=c("GL.account")) |>
|
||||
mutate(act.req=ifelse(devia.per>Limit & devia > 500,T,F)) |>
|
||||
filter(act.req) |>
|
||||
select(1:3) |>
|
||||
mutate(explanation=c("Cyberattack","Overwork","Interview","Maintenance","Quarterly Report","New Legislation"))
|
||||
|
||||
dbWriteTable(connec, "explanations", exp, append=TRUE)
|
||||
exp<-dbGetQuery(
|
||||
connec,
|
||||
'SELECT * FROM explanations'
|
||||
)
|
||||
|
||||
approvals<- exp |> mutate(approved=F)
|
||||
|
||||
dbWriteTable(connec, "approvals", approvals, overwrite=T)
|
||||
|
||||
approvals<-dbGetQuery(
|
||||
connec,
|
||||
'SELECT * FROM approvals'
|
||||
)
|
||||
|
||||
|
||||
dat |>
|
||||
inner_join(lims, by=c("GL.account")) |>
|
||||
mutate(act.req=ifelse(devia.per>Limit & devia > 500,T,F)) |>
|
||||
filter(act.req) |>
|
||||
left_join(exp, by=c("month"="month","Cost.center"="Cost.center","GL.account"="GL.account"))
|
||||
|
||||
|
||||
emailsids<-dat |>
|
||||
select(Cost.center, GL.account) |>
|
||||
distinct() |>
|
||||
mutate(email=c("asitav.sen@lanubia.com"))
|
||||
dbWriteTable(connec, "emails", emailsids, overwrite=T)
|
||||
emailsids<-dbGetQuery(
|
||||
connec,
|
||||
'SELECT * FROM emails'
|
||||
)
|
||||
|
||||
sel.emails<- emailsids |>
|
||||
filter(email=="asitav.sen@lanubia.com")
|
||||
|
||||
dat |>
|
||||
inner_join(lims, by=c("GL.account")) |>
|
||||
mutate(act.req=ifelse(devia.per>Limit & devia > 500,T,F)) |>
|
||||
filter(act.req) |>
|
||||
left_join(sel.emails, by=c("Cost.center"="Cost.center","GL.account"="GL.account")) |>
|
||||
left_join(exp, by=c("month"="month","Cost.center"="Cost.center","GL.account"="GL.account")) |>
|
||||
filter(is.na(explanation) | explanation=="") |>
|
||||
filter(email=="asitav.sen@lanubia.com") |>
|
||||
select(-c(9,10))
|
||||
|
||||
exp
|
||||
```
|
||||
|
||||
|
||||
|
||||
```{r}
|
||||
|
||||
dat.2<-
|
||||
dat |>
|
||||
mutate(month=ym(month)) |>
|
||||
group_by(month) |>
|
||||
summarise(Plan=sum(Plan),Actual=sum(Actual)) |>
|
||||
mutate(devia=Actual-Plan) |>
|
||||
mutate(deviation.percent=round(devia*100/Plan,2))
|
||||
|
||||
e_chart(dat.2, x=month) |>
|
||||
e_line(serie = deviation.percent, smooth=T, color="cyan") |>
|
||||
e_area(serie = deviation.percent, smooth=T, color="gray") |>
|
||||
e_axis_labels(x = "month", y="Deviation") |>
|
||||
e_format_y_axis(suffix = " %") |>
|
||||
e_title("Deviation", "Selected Cost Centers") |>
|
||||
e_tooltip() |>
|
||||
e_legend(right = 100) |>
|
||||
e_datazoom(x_index = c(0, 1)) |>
|
||||
e_toolbox_feature(feature = c("saveAsImage","dataView")) |>
|
||||
e_theme("chalk")
|
||||
|
||||
history<-dat.2 |> select(month,deviation.percent) |> rename(ds=month, y=deviation.percent)
|
||||
model <- prophet::prophet(history)
|
||||
future <- prophet::make_future_dataframe(model, periods = 2)
|
||||
forecast <- predict(model, future)
|
||||
|
||||
dat |>
|
||||
filter(month==max(month)) |>
|
||||
mutate(cost_gl=paste0(Cost.center,"_",GL.account)) |>
|
||||
group_by(cost_gl) |>
|
||||
summarise(Plan=sum(Plan),Actual=sum(Actual)) |>
|
||||
mutate(devia=Actual-Plan) |>
|
||||
mutate(deviation.percent=round(devia*100/Plan,2)) |>
|
||||
arrange(desc(deviation.percent)) |>
|
||||
e_charts(cost_gl) |>
|
||||
e_bar(Plan, name = "Plan", color="gray") |>
|
||||
e_step(Actual, name = "Actual", color="red") |>
|
||||
e_axis_labels(x = "GL+Cost Center", y="Deviation") |>
|
||||
e_title("Selected Cost Centers") |>
|
||||
e_tooltip() |>
|
||||
e_legend(right = 100) |>
|
||||
e_datazoom(x_index = 0, type = "slider") |>
|
||||
e_datazoom(y_index = 0, type = "slider") |>
|
||||
e_toolbox_feature(feature = c("saveAsImage","dataView")) |>
|
||||
e_theme("chalk")
|
||||
```
|
||||
```{r}
|
||||
unique(dat$Cost.center) |> saveRDS("./data/costcenters.RDS")
|
||||
unique(dat$GL.account) |> saveRDS("./data/glaccounts.RDS")
|
||||
unique(dat$month) |> saveRDS("./data/months.RDS")
|
||||
lims
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```{r}
|
||||
dat.dev<-dat |>
|
||||
filter(devia>0) |>
|
||||
select(2,3,6)
|
||||
|
||||
e_charts(dat.dev) |>
|
||||
e_pie(devia)
|
||||
|
||||
blastula::create_smtp_creds_file(
|
||||
file = "email_creds",
|
||||
user = "apikey",
|
||||
host = "smtp.sendgrid.net",
|
||||
port = 465,
|
||||
use_ssl = TRUE
|
||||
)
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
13
bUdgEtRack.Rproj
Normal file
13
bUdgEtRack.Rproj
Normal file
@@ -0,0 +1,13 @@
|
||||
Version: 1.0
|
||||
|
||||
RestoreWorkspace: Default
|
||||
SaveWorkspace: Default
|
||||
AlwaysSaveHistory: Default
|
||||
|
||||
EnableCodeIndexing: Yes
|
||||
UseSpacesForTab: Yes
|
||||
NumSpacesForTab: 2
|
||||
Encoding: UTF-8
|
||||
|
||||
RnwWeave: Sweave
|
||||
LaTeX: pdfLaTeX
|
||||
BIN
cred.sqlite
Normal file
BIN
cred.sqlite
Normal file
Binary file not shown.
23
data/Jul22.csv
Normal file
23
data/Jul22.csv
Normal file
@@ -0,0 +1,23 @@
|
||||
month,Cost center,GL account,Plan,Actual
|
||||
202205,IT,Salaries,30000,29000
|
||||
202205,IT,Business travel,5000,6000
|
||||
202205,IT,Consultancy,10000,10500
|
||||
202205,IT,Training costs,5000,6000
|
||||
202205,HR local support,Salaries,15000,13000
|
||||
202205,HR local support,Events,10000,9000
|
||||
202205,HR local support,Training costs,2000,2700
|
||||
202205,Finance,Salaries,13000,11000
|
||||
202205,Finance,Consultancy,6000,5000
|
||||
202205,Finance,Training costs,500,500
|
||||
202205,Purchasing,Salaries,40000,40000
|
||||
202205,Purchasing,Consultancy,2000,1500
|
||||
202205,Purchasing,Training costs,1000,1500
|
||||
202205,HR services,Salaries,35000,39000
|
||||
202205,HR services,Business travel,10000,9000
|
||||
202205,HR services,Training costs,2000,1000
|
||||
202205,Public Relations,Salaries,10000,12000
|
||||
202205,Public Relations,Events,18000,19200
|
||||
202205,Public Relations,Business travel,2000,1000
|
||||
202205,Public Relations,Training costs,600,700
|
||||
202205,Facility management,Office lease,50000,50000
|
||||
202205,Facility management,Salaries,5000,4000
|
||||
|
23
data/Jun22.csv
Normal file
23
data/Jun22.csv
Normal file
@@ -0,0 +1,23 @@
|
||||
month,Cost center,GL account,Plan,Actual
|
||||
202205,IT,Salaries,30000,33000
|
||||
202205,IT,Business travel,5000,4000
|
||||
202205,IT,Consultancy,10000,11000
|
||||
202205,IT,Training costs,5000,6000
|
||||
202205,HR local support,Salaries,15000,16000
|
||||
202205,HR local support,Events,10000,8000
|
||||
202205,HR local support,Training costs,2000,3000
|
||||
202205,Finance,Salaries,13000,15000
|
||||
202205,Finance,Consultancy,6000,5000
|
||||
202205,Finance,Training costs,500,500
|
||||
202205,Purchasing,Salaries,40000,42000
|
||||
202205,Purchasing,Consultancy,2000,3000
|
||||
202205,Purchasing,Training costs,1000,2000
|
||||
202205,HR services,Salaries,35000,39000
|
||||
202205,HR services,Business travel,10000,9000
|
||||
202205,HR services,Training costs,2000,1000
|
||||
202205,Public Relations,Salaries,10000,12000
|
||||
202205,Public Relations,Events,18000,19000
|
||||
202205,Public Relations,Business travel,2000,1000
|
||||
202205,Public Relations,Training costs,600,600
|
||||
202205,Facility management,Office lease,50000,50000
|
||||
202205,Facility management,Salaries,5000,4000
|
||||
|
23
data/apr22.csv
Normal file
23
data/apr22.csv
Normal file
@@ -0,0 +1,23 @@
|
||||
month,Cost center,GL account,Plan,Actual
|
||||
202204,IT,Salaries,30000,31000
|
||||
202204,IT,Business travel,5000,6000
|
||||
202204,IT,Consultancy,10000,10500
|
||||
202204,IT,Training costs,5000,5000
|
||||
202204,HR local support,Salaries,15000,13000
|
||||
202204,HR local support,Events,10000,9000
|
||||
202204,HR local support,Training costs,2000,2700
|
||||
202204,Finance,Salaries,13000,15000
|
||||
202204,Finance,Consultancy,6000,5000
|
||||
202204,Finance,Training costs,500,500
|
||||
202204,Purchasing,Salaries,40000,40000
|
||||
202204,Purchasing,Consultancy,2000,1500
|
||||
202204,Purchasing,Training costs,1000,1500
|
||||
202204,HR services,Salaries,35000,39000
|
||||
202204,HR services,Business travel,10000,9000
|
||||
202204,HR services,Training costs,2000,1000
|
||||
202204,Public Relations,Salaries,10000,10000
|
||||
202204,Public Relations,Events,18000,19200
|
||||
202204,Public Relations,Business travel,2000,1000
|
||||
202204,Public Relations,Training costs,600,600
|
||||
202204,Facility management,Office lease,50000,50000
|
||||
202204,Facility management,Salaries,5000,4000
|
||||
|
BIN
data/costcenters.RDS
Normal file
BIN
data/costcenters.RDS
Normal file
Binary file not shown.
7
data/dev.limits.csv
Normal file
7
data/dev.limits.csv
Normal file
@@ -0,0 +1,7 @@
|
||||
GL account,Limit
|
||||
Office lease,0.15
|
||||
Events,0.10
|
||||
Consultancy,0.10
|
||||
Business travel,0.10
|
||||
Salaries,0.10
|
||||
Training costs,0.15
|
||||
|
1
data/email_creds
Normal file
1
data/email_creds
Normal file
@@ -0,0 +1 @@
|
||||
{"type":"list","attributes":{"names":{"type":"character","attributes":{},"value":["version","host","port","use_ssl","user","password"]}},"value":[{"type":"integer","attributes":{},"value":[1]},{"type":"character","attributes":{},"value":["smtp.sendgrid.net"]},{"type":"double","attributes":{},"value":[465]},{"type":"logical","attributes":{},"value":[true]},{"type":"character","attributes":{},"value":["apikey"]},{"type":"character","attributes":{},"value":["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"]}]}
|
||||
BIN
data/glaccounts.RDS
Normal file
BIN
data/glaccounts.RDS
Normal file
Binary file not shown.
67
data/initial.csv
Normal file
67
data/initial.csv
Normal file
@@ -0,0 +1,67 @@
|
||||
month,Cost center,GL account,Plan,Actual
|
||||
202201,IT,Salaries,30000,29000
|
||||
202201,IT,Business travel,5000,6150
|
||||
202201,IT,Consultancy,10000,12000
|
||||
202201,IT,Training costs,5000,4800
|
||||
202201,HR local support,Salaries,15000,15500
|
||||
202201,HR local support,Events,5000,4900
|
||||
202201,HR local support,Training costs,2000,1500
|
||||
202201,Finance,Salaries,13000,13000
|
||||
202201,Finance,Consultancy,6000,6500
|
||||
202201,Finance,Training costs,4000,4500
|
||||
202201,Purchasing,Salaries,40000,41500
|
||||
202201,Purchasing,Consultancy,2000,2000
|
||||
202201,Purchasing,Training costs,1000,1000
|
||||
202201,HR services,Salaries,35000,36000
|
||||
202201,HR services,Business travel,10000,14000
|
||||
202201,HR services,Training costs,2000,1800
|
||||
202201,Public Relations,Salaries,10000,8000
|
||||
202201,Public Relations,Events,18000,19000
|
||||
202201,Public Relations,Business travel,2000,1950
|
||||
202201,Public Relations,Training costs,600,500
|
||||
202201,Facility management,Office lease,50000,48000
|
||||
202201,Facility management,Salaries,5000,4500
|
||||
202202,IT,Salaries,30000,29000
|
||||
202202,IT,Business travel,5000,5000
|
||||
202202,IT,Consultancy,10000,9000
|
||||
202202,IT,Training costs,5000,5000
|
||||
202202,HR local support,Salaries,15000,15500
|
||||
202202,HR local support,Events,0,0
|
||||
202202,HR local support,Training costs,2000,2200
|
||||
202202,Finance,Salaries,13000,13000
|
||||
202202,Finance,Consultancy,6000,5000
|
||||
202202,Finance,Training costs,0,0
|
||||
202202,Purchasing,Salaries,40000,41500
|
||||
202202,Purchasing,Consultancy,2000,2000
|
||||
202202,Purchasing,Training costs,1000,0
|
||||
202202,HR services,Salaries,35000,36000
|
||||
202202,HR services,Business travel,10000,7000
|
||||
202202,HR services,Training costs,2000,2000
|
||||
202202,Public Relations,Salaries,10000,8000
|
||||
202202,Public Relations,Events,18000,10000
|
||||
202202,Public Relations,Business travel,2000,2200
|
||||
202202,Public Relations,Training costs,600,600
|
||||
202202,Facility management,Office lease,50000,48000
|
||||
202202,Facility management,Salaries,5000,4500
|
||||
202203,IT,Salaries,30000,30000
|
||||
202203,IT,Business travel,5000,6000
|
||||
202203,IT,Consultancy,10000,10500
|
||||
202203,IT,Training costs,5000,5000
|
||||
202203,HR local support,Salaries,15000,15000
|
||||
202203,HR local support,Events,10000,5000
|
||||
202203,HR local support,Training costs,2000,1000
|
||||
202203,Finance,Salaries,13000,13000
|
||||
202203,Finance,Consultancy,6000,7000
|
||||
202203,Finance,Training costs,500,500
|
||||
202203,Purchasing,Salaries,40000,40000
|
||||
202203,Purchasing,Consultancy,2000,2000
|
||||
202203,Purchasing,Training costs,1000,1000
|
||||
202203,HR services,Salaries,35000,35000
|
||||
202203,HR services,Business travel,10000,10000
|
||||
202203,HR services,Training costs,2000,3000
|
||||
202203,Public Relations,Salaries,10000,10600
|
||||
202203,Public Relations,Events,18000,18000
|
||||
202203,Public Relations,Business travel,2000,1000
|
||||
202203,Public Relations,Training costs,600,1000
|
||||
202203,Facility management,Office lease,50000,50000
|
||||
202203,Facility management,Salaries,5000,5000
|
||||
|
23
data/may22.csv
Normal file
23
data/may22.csv
Normal file
@@ -0,0 +1,23 @@
|
||||
month,Cost center,GL account,Plan,Actual
|
||||
202205,IT,Salaries,30000,31000
|
||||
202205,IT,Business travel,5000,6000
|
||||
202205,IT,Consultancy,10000,10500
|
||||
202205,IT,Training costs,5000,5000
|
||||
202205,HR local support,Salaries,15000,13000
|
||||
202205,HR local support,Events,10000,9000
|
||||
202205,HR local support,Training costs,2000,2700
|
||||
202205,Finance,Salaries,13000,15000
|
||||
202205,Finance,Consultancy,6000,5000
|
||||
202205,Finance,Training costs,500,500
|
||||
202205,Purchasing,Salaries,40000,40000
|
||||
202205,Purchasing,Consultancy,2000,1500
|
||||
202205,Purchasing,Training costs,1000,1500
|
||||
202205,HR services,Salaries,35000,39000
|
||||
202205,HR services,Business travel,10000,9000
|
||||
202205,HR services,Training costs,2000,1000
|
||||
202205,Public Relations,Salaries,10000,10000
|
||||
202205,Public Relations,Events,18000,19200
|
||||
202205,Public Relations,Business travel,2000,1000
|
||||
202205,Public Relations,Training costs,600,600
|
||||
202205,Facility management,Office lease,50000,50000
|
||||
202205,Facility management,Salaries,5000,4000
|
||||
|
BIN
data/may22.xlsx
Normal file
BIN
data/may22.xlsx
Normal file
Binary file not shown.
BIN
data/months.RDS
Normal file
BIN
data/months.RDS
Normal file
Binary file not shown.
1
email_creds
Normal file
1
email_creds
Normal file
@@ -0,0 +1 @@
|
||||
{"type":"list","attributes":{"names":{"type":"character","attributes":{},"value":["version","host","port","use_ssl","user","password"]}},"value":[{"type":"integer","attributes":{},"value":[1]},{"type":"character","attributes":{},"value":["smtp.sendgrid.net"]},{"type":"double","attributes":{},"value":[465]},{"type":"logical","attributes":{},"value":[true]},{"type":"character","attributes":{},"value":["apikey"]},{"type":"character","attributes":{},"value":["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"]}]}
|
||||
87
helper_server.R
Normal file
87
helper_server.R
Normal file
@@ -0,0 +1,87 @@
|
||||
|
||||
|
||||
|
||||
# Database details
|
||||
|
||||
|
||||
dsn_database <- "postgres"
|
||||
|
||||
dsn_hostname <- "localhost"
|
||||
|
||||
dsn_port <- "5432"
|
||||
|
||||
dsn_uid <- "postgres"
|
||||
|
||||
dsn_pwd <- "julley09"
|
||||
|
||||
|
||||
|
||||
|
||||
# Read Cost Center etc. list from data (Changes, modification needs creating the file again and redeploying the app)
|
||||
|
||||
cost.centers <- readRDS("./data/costcenters.RDS")
|
||||
glaccounts <- readRDS("./data/glaccounts.RDS")
|
||||
mont <- readRDS("./data/months.RDS")
|
||||
|
||||
# Email settings
|
||||
|
||||
# smtp <- server(
|
||||
# host = "smtp.sendgrid.net",
|
||||
# port = 465,
|
||||
# username = "apikey",
|
||||
# password = "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"
|
||||
# )
|
||||
|
||||
|
||||
# Function to get data from DB
|
||||
|
||||
get.db.data <- function(qry = 'SELECT * FROM calculated') {
|
||||
dbGetQuery(connec,
|
||||
qry)
|
||||
}
|
||||
|
||||
# Function to aggregate monthly deviation
|
||||
|
||||
mon.dev <- function(dat) {
|
||||
dat |>
|
||||
mutate(month = ym(month)) |>
|
||||
group_by(month) |>
|
||||
summarise(Plan = sum(Plan), Actual = sum(Actual)) |>
|
||||
mutate(devia = Actual - Plan) |>
|
||||
mutate(deviation.percent = round(devia * 100 / Plan, 2))
|
||||
}
|
||||
|
||||
# Function to aggregate last month by gl and cost
|
||||
|
||||
last.mon <- function(dat) {
|
||||
dat |>
|
||||
filter(month == max(month)) |>
|
||||
mutate(cost_gl = paste0(Cost.center, "_", GL.account)) |>
|
||||
group_by(cost_gl) |>
|
||||
summarise(Plan = sum(Plan), Actual = sum(Actual)) |>
|
||||
mutate(devia = Actual - Plan) |>
|
||||
mutate(deviation.percent = round(devia * 100 / Plan, 2)) |>
|
||||
arrange(desc(deviation.percent))
|
||||
}
|
||||
|
||||
|
||||
admin.menu <- sidebarMenu(
|
||||
id = "m",
|
||||
menuItem("Dashboard", tabName = "dashboard", icon = icon("chart-line")),
|
||||
menuItem("Upload", icon = icon("upload"), tabName = "upload"),
|
||||
menuItem("Approvals", icon = icon("check"), tabName = "approvals"),
|
||||
menuItem("Explanations", icon = icon("file"), tabName = "explanations"),
|
||||
menuItem("Admin", icon = icon("toolbox"), tabName = "admin"),
|
||||
menuItem("Contact us", icon = icon("at"), href = "https://lanubia.com/contact/")
|
||||
)
|
||||
|
||||
other.menu <- sidebarMenu(
|
||||
id = "m",
|
||||
menuItem(
|
||||
"Dashboard",
|
||||
tabName = "dashboard",
|
||||
icon = icon("dashboard")
|
||||
),
|
||||
menuItem("Explanations", icon = icon("th"), tabName = "explanations"),
|
||||
menuItem("Contact us", icon = icon("id-card"), href = "https://lanubia.com/contact/")
|
||||
)
|
||||
169
helper_ui.R
Normal file
169
helper_ui.R
Normal file
@@ -0,0 +1,169 @@
|
||||
|
||||
dashboard<- tabItem(
|
||||
tabName = "dashboard",
|
||||
autoWaiter(),
|
||||
fluidRow(
|
||||
column(
|
||||
width = 4,
|
||||
"Filter Cost Center and GL Accounts"
|
||||
),
|
||||
column(
|
||||
width = 4,
|
||||
pickerInput(
|
||||
"glaccount",
|
||||
choices = glaccounts,
|
||||
selected = glaccounts,
|
||||
options = list(
|
||||
`actions-box` = TRUE),
|
||||
multiple = TRUE
|
||||
)
|
||||
),
|
||||
column(
|
||||
width = 4,
|
||||
pickerInput(
|
||||
"costcenter",
|
||||
choices = cost.centers, # Getting the list from reading data from local. See helper_server
|
||||
selected = cost.centers,
|
||||
options = list(
|
||||
`actions-box` = TRUE),
|
||||
multiple = TRUE
|
||||
)
|
||||
)
|
||||
),
|
||||
fluidRow(
|
||||
valueUI("variation"),
|
||||
valueUI("variabs"),
|
||||
valueUI("actexpe"),
|
||||
),
|
||||
fluidRow(
|
||||
shinydashboardPlus::box(
|
||||
title = "Deviation % by Month",
|
||||
collapsible = TRUE,
|
||||
status = "navy",
|
||||
solidHeader = TRUE,
|
||||
echarts4rOutput("monthlypervar",height = "300px")
|
||||
),
|
||||
shinydashboardPlus::box(
|
||||
title = "Abs. Deviation by Month",
|
||||
collapsible = TRUE,
|
||||
status = "navy",
|
||||
solidHeader = TRUE,
|
||||
echarts4rOutput("monthlyabsvar",height = "300px")
|
||||
)
|
||||
),
|
||||
fluidRow(
|
||||
shinydashboardPlus::box(
|
||||
title = "Plan vs Actual",
|
||||
collapsible = TRUE,
|
||||
status = "navy",
|
||||
solidHeader = TRUE,
|
||||
sidebar = boxSidebar(
|
||||
startOpen = FALSE,
|
||||
id = "monthselector",
|
||||
pickerInput(
|
||||
"mont",
|
||||
choices = mont,
|
||||
selected = max(mont)
|
||||
)
|
||||
),
|
||||
echarts4rOutput("monthlyabs",height = "300px")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
upload<- tabItem(
|
||||
tabName = "upload",
|
||||
autoWaiter(),
|
||||
fluidRow(
|
||||
|
||||
shinydashboardPlus::box(
|
||||
title = "Fresh Upload",
|
||||
collapsible = TRUE,
|
||||
width = 12,
|
||||
status = "navy",
|
||||
solidHeader = TRUE,
|
||||
fileInput(
|
||||
"inpfile",
|
||||
"Upload the file",
|
||||
multiple = FALSE,
|
||||
accept = ".csv",
|
||||
width = NULL,
|
||||
buttonLabel = "Browse...",
|
||||
placeholder = "No file selected"
|
||||
),
|
||||
datatableUI("freshdat"),
|
||||
actionBttn("datsubmit","Submit")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
appndev <- tabItem(
|
||||
tabName = "approvals",
|
||||
autoWaiter(),
|
||||
shinydashboardPlus::box(
|
||||
title = "Deviations without explanation",
|
||||
collapsible = TRUE,
|
||||
width = 12,
|
||||
status = "navy",
|
||||
solidHeader = TRUE,
|
||||
sidebar = boxSidebar(
|
||||
startOpen = FALSE,
|
||||
id = "limitselector",
|
||||
sliderInput("limittoignore","Define limit", min=100, max=2000, step=100, value = 500)
|
||||
),
|
||||
datatableUI("devnoexp"),
|
||||
actionBttn("notify","Notify")
|
||||
),
|
||||
shinydashboardPlus::box(
|
||||
title = "Awaiting Approval",
|
||||
collapsible = TRUE,
|
||||
width = 12,
|
||||
status = "navy",
|
||||
solidHeader = TRUE,
|
||||
excelOutput("expnoapp"),
|
||||
actionBttn("approvalsubmit","Submit")
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
explan <- tabItem(
|
||||
tabName = "explanations",
|
||||
autoWaiter(),
|
||||
shinydashboardPlus::box(
|
||||
title = "Explanations Pending",
|
||||
collapsible = TRUE,
|
||||
width = 12,
|
||||
status = "navy",
|
||||
solidHeader = TRUE,
|
||||
excelOutput("explanationtofill"),
|
||||
actionBttn("explanationsubmit","Submit")
|
||||
)
|
||||
|
||||
|
||||
)
|
||||
|
||||
admin<-tabItem(
|
||||
tabName = "admin",
|
||||
autoWaiter(),
|
||||
shinydashboardPlus::box(
|
||||
title = "Email IDs",
|
||||
width = 8,
|
||||
collapsible = TRUE,
|
||||
status = "navy",
|
||||
solidHeader = TRUE,
|
||||
excelOutput("emailtable"),
|
||||
actionBttn("emailsubmit","Submit")
|
||||
),
|
||||
shinydashboardPlus::box(
|
||||
title = "Deviation Rules",
|
||||
width = 4,
|
||||
collapsible = TRUE,
|
||||
status = "navy",
|
||||
solidHeader = TRUE,
|
||||
excelOutput("limitable"),
|
||||
actionBttn("limitsubmit","Submit")
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
|
||||
29
mod_datatable.R
Normal file
29
mod_datatable.R
Normal file
@@ -0,0 +1,29 @@
|
||||
datatableUI<- function(id){
|
||||
ns<-NS(id)
|
||||
DT::dataTableOutput(ns("dtable"))
|
||||
}
|
||||
|
||||
datatableServer<-function(id, dat){
|
||||
moduleServer(
|
||||
id,
|
||||
function(input,output,session){
|
||||
output$dtable<- renderDataTable({
|
||||
validate(need(nrow(dat())>0, "No data"))
|
||||
datatable(
|
||||
dat(),
|
||||
extensions = "Buttons",
|
||||
options = list(
|
||||
paging = TRUE,
|
||||
scrollX = TRUE,
|
||||
searching = TRUE,
|
||||
ordering = TRUE,
|
||||
dom = 'Bfrtip',
|
||||
buttons = c('copy', 'csv', 'excel', 'pdf'),
|
||||
pageLength = 10,
|
||||
lengthMenu = c(3, 5, 10)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
32
mod_elinearea.R
Normal file
32
mod_elinearea.R
Normal file
@@ -0,0 +1,32 @@
|
||||
elineareaUI<- function(id){
|
||||
ns<-NS(id)
|
||||
echarts4rOutput("elaplot")
|
||||
}
|
||||
|
||||
elineareaServer<-function(id, dat){
|
||||
moduleServer(
|
||||
id,
|
||||
function(input,output,session){
|
||||
output$elaplot<- renderEcharts4r({
|
||||
dat |>
|
||||
e_chart(x=month) |>
|
||||
e_line(serie = deviation.percent, smooth=T, color="cyan") |>
|
||||
e_area(serie = deviation.percent, smooth=T, color="cyan") |>
|
||||
e_axis_labels(x = "month", y="Deviation") |>
|
||||
e_format_y_axis(suffix = " %") |>
|
||||
e_title("Deviation", "Selected Cost Centers") |>
|
||||
e_tooltip(formatter = htmlwidgets::JS("
|
||||
function(params){
|
||||
return('Month: ' + params.value[0] + '<br />Deviation: ' + params.value[1] + '%')
|
||||
}
|
||||
")
|
||||
) |>
|
||||
e_legend(right = 100) |>
|
||||
e_datazoom(x_index = c(0, 1)) |>
|
||||
e_toolbox_feature(feature = c("saveAsImage","dataView")) |>
|
||||
e_theme("forest")
|
||||
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
27
mod_step.R
Normal file
27
mod_step.R
Normal file
@@ -0,0 +1,27 @@
|
||||
stepUI<- function(id){
|
||||
ns<-NS(id)
|
||||
echarts4rOutput("elaplot")
|
||||
}
|
||||
|
||||
stepServer<-function(id, dat){
|
||||
moduleServer(
|
||||
id,
|
||||
function(input,output,session){
|
||||
output$elaplot<- renderEcharts4r({
|
||||
dat |>
|
||||
e_charts(cost_gl) |>
|
||||
e_bar(Plan, name = "Plan", color="gray") |>
|
||||
e_step(Actual, name = "Actual", color="red") |>
|
||||
e_axis_labels(x = "GL+Cost Center", y="Deviation") |>
|
||||
e_title("Selected Cost Centers") |>
|
||||
e_tooltip() |>
|
||||
e_legend(right = 100) |>
|
||||
e_datazoom(x_index = 0, type = "slider") |>
|
||||
e_datazoom(y_index = 0, type = "slider") |>
|
||||
e_toolbox_feature(feature = c("saveAsImage","dataView")) |>
|
||||
e_theme("chalk")
|
||||
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
24
mod_valuebox.R
Normal file
24
mod_valuebox.R
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
valueUI<- function(id){
|
||||
ns<-NS(id)
|
||||
valueBoxOutput(ns("vbox"))
|
||||
}
|
||||
|
||||
valueServer<-function(id, ttl="title",n,icn="credit-card",clr="red",symbl){
|
||||
moduleServer(
|
||||
id,
|
||||
function(input,output,session){
|
||||
#print(n())
|
||||
output$vbox<- renderValueBox({
|
||||
valueBox(
|
||||
paste0(n," ", symbl),
|
||||
ttl,
|
||||
icon=icon(icn),
|
||||
color = clr
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
568
server.R
Normal file
568
server.R
Normal file
@@ -0,0 +1,568 @@
|
||||
# Server logic
|
||||
# Budget variation tracking of Aramco
|
||||
# ::::::Asitav Sen::::::
|
||||
# ::LaNubia Consulting::
|
||||
|
||||
library(shiny)
|
||||
|
||||
# Define server logic
|
||||
shinyServer(function(input, output, session) {
|
||||
res_auth <- secure_server(
|
||||
check_credentials = check_credentials("cred.sqlite",
|
||||
passphrase = "kJuyhG657Hj&^%gshj*762hjsknh&662"),
|
||||
keep_token=TRUE
|
||||
)
|
||||
|
||||
output$menu <- renderMenu({
|
||||
if (res_auth$admin == FALSE) {
|
||||
other.menu
|
||||
} else
|
||||
admin.menu
|
||||
})
|
||||
|
||||
# Connect to DB
|
||||
connec <- dbConnect(
|
||||
RPostgres::Postgres(),
|
||||
dbname = dsn_database,
|
||||
host = dsn_hostname,
|
||||
port = dsn_port,
|
||||
user = dsn_uid,
|
||||
password = dsn_pwd
|
||||
)
|
||||
|
||||
# On stop disconnect from DB
|
||||
onStop(function() {
|
||||
dbDisconnect(connec)
|
||||
})
|
||||
|
||||
# Getting the data from DB
|
||||
dat <- reactive({
|
||||
dbGetQuery(connec,
|
||||
'SELECT * FROM calculated')
|
||||
#get.db.data('SELECT * FROM calculated')
|
||||
})
|
||||
|
||||
# Getting deviation limits from DB
|
||||
lims <- reactive({
|
||||
dbGetQuery(connec,
|
||||
'SELECT * FROM limits')
|
||||
#get.db.data('SELECT * FROM limits')
|
||||
})
|
||||
|
||||
# Getting the explanations from DB
|
||||
|
||||
exp <- reactive({
|
||||
dbGetQuery(connec,
|
||||
'SELECT * FROM explanations')
|
||||
#get.db.data('SELECT * FROM explanations')
|
||||
})
|
||||
|
||||
# Getting approval Table from DB
|
||||
approvals <- reactive({
|
||||
dbGetQuery(connec,
|
||||
'SELECT * FROM approvals')
|
||||
#get.db.data('SELECT * FROM approvals')
|
||||
})
|
||||
|
||||
# Getting email ids
|
||||
|
||||
emailids <- reactive({
|
||||
dbGetQuery(connec,
|
||||
'SELECT * FROM emails')
|
||||
#get.db.data('SELECT * FROM emails')
|
||||
})
|
||||
|
||||
|
||||
dat.need.exp <- reactive({
|
||||
req(dat())
|
||||
dat() |>
|
||||
inner_join(lims(), by = c("GL.account")) |>
|
||||
mutate(act.req = ifelse(devia.per > Limit &
|
||||
devia > input$limittoignore, T, F)) |>
|
||||
filter(act.req) |>
|
||||
left_join(
|
||||
exp(),
|
||||
by = c(
|
||||
"month" = "month",
|
||||
"Cost.center" = "Cost.center",
|
||||
"GL.account" = "GL.account"
|
||||
)
|
||||
) |>
|
||||
filter(is.na(explanation) | explanation == "")
|
||||
})
|
||||
|
||||
# Update the gl account selector
|
||||
observeEvent(input$costcenter, {
|
||||
req(dat())
|
||||
choi <- dat() |>
|
||||
filter(Cost.center %in% input$costcenter) |>
|
||||
select(GL.account) |> distinct() |> pull()
|
||||
updatePickerInput(
|
||||
session = session,
|
||||
inputId = "glaccount",
|
||||
choices = choi,
|
||||
selected = choi
|
||||
)
|
||||
|
||||
})
|
||||
|
||||
|
||||
observeEvent(c(input$costcenter, input$glaccount), {
|
||||
# Creating data that will be filtered using the GL and cost center selection
|
||||
dat.filtered <- dat() |>
|
||||
filter(Cost.center %in% input$costcenter) |>
|
||||
filter(GL.account %in% input$glaccount) |>
|
||||
mon.dev() |>
|
||||
filter(month == max(month))
|
||||
last.var.per <- dat.filtered |>
|
||||
pull(deviation.percent) #Last variation percent
|
||||
|
||||
last.var.abs <- dat.filtered |>
|
||||
pull(devia) #Last variation absolute val
|
||||
|
||||
last.exp.act <- dat.filtered |>
|
||||
pull(Actual) #Last expense
|
||||
|
||||
valueServer(
|
||||
"variation",
|
||||
ttl = "Latest Variation %",
|
||||
n = last.var.per,
|
||||
icn = "percent",
|
||||
clr = "red",
|
||||
symbl = "%"
|
||||
)
|
||||
|
||||
valueServer(
|
||||
"variabs",
|
||||
ttl = "Last Variation Amount",
|
||||
n = last.var.abs,
|
||||
icn = "dollar-sign",
|
||||
clr = "green",
|
||||
symbl = "$"
|
||||
)
|
||||
|
||||
valueServer(
|
||||
"actexpe",
|
||||
ttl = "Last Expense",
|
||||
n = last.exp.act,
|
||||
icn = "coins",
|
||||
clr = "blue",
|
||||
symbl = "$"
|
||||
)
|
||||
|
||||
})
|
||||
|
||||
# Data selected after aplying the filters of cost center and gl account
|
||||
dat.selected <- reactive({
|
||||
req(dat())
|
||||
req(input$costcenter)
|
||||
req(input$glaccount)
|
||||
|
||||
dat() |>
|
||||
filter(Cost.center %in% input$costcenter) |>
|
||||
filter(GL.account %in% input$glaccount)
|
||||
})
|
||||
|
||||
# Monthly aggregate
|
||||
dat.monthly.per <- reactive({
|
||||
req(dat.selected())
|
||||
dat.selected() |>
|
||||
mutate(month = ym(month)) |>
|
||||
group_by(month) |>
|
||||
summarise(Plan = sum(Plan), Actual = sum(Actual)) |>
|
||||
mutate(devia = Actual - Plan) |>
|
||||
mutate(deviation.percent = round(devia * 100 / Plan, 2))
|
||||
})
|
||||
|
||||
|
||||
|
||||
# Monthly percentage deviation
|
||||
output$monthlypervar <- renderEcharts4r({
|
||||
req(dat.monthly.per())
|
||||
|
||||
|
||||
e_chart(dat.monthly.per(), x = month) |>
|
||||
e_line(serie = deviation.percent,
|
||||
smooth = T,
|
||||
color = "cyan", opacity=0.8) |>
|
||||
e_area(serie = deviation.percent,
|
||||
smooth = T,
|
||||
color = "gray", opacity=0.6) |>
|
||||
e_axis_labels(x = "month", y = "Deviation") |>
|
||||
e_format_y_axis(suffix = " %") |>
|
||||
e_tooltip() |>
|
||||
e_legend(right = 100) |>
|
||||
e_datazoom(x_index = c(0, 1)) |>
|
||||
e_image_g(
|
||||
right = 50,
|
||||
top = 20,
|
||||
z = -999,
|
||||
style = list(
|
||||
image = "logo.png",
|
||||
width = 75,
|
||||
height = 75,
|
||||
opacity = .6
|
||||
)
|
||||
) |>
|
||||
e_toolbox_feature(feature = c("saveAsImage", "dataView")) |>
|
||||
e_theme("roma")
|
||||
})
|
||||
|
||||
# Monthly Absolute Deviation
|
||||
dat.monthly.abs <- reactive({
|
||||
dat.selected() |>
|
||||
filter(month == input$mont) |>
|
||||
mutate(cost_gl = paste0(Cost.center, "_", GL.account)) |>
|
||||
group_by(cost_gl) |>
|
||||
summarise(Plan = sum(Plan), Actual = sum(Actual)) |>
|
||||
mutate(devia = Actual - Plan) |>
|
||||
mutate(deviation.percent = round(devia * 100 / Plan, 2)) |>
|
||||
arrange(desc(deviation.percent))
|
||||
})
|
||||
|
||||
# Monthly absolute by cost center + gl account
|
||||
output$monthlyabs <- renderEcharts4r({
|
||||
req(dat.monthly.abs())
|
||||
dat.monthly.abs() |>
|
||||
e_charts(cost_gl) |>
|
||||
e_bar(Plan, name = "Plan", color = "gray", opacity=0.6) |>
|
||||
e_step(Actual, name = "Actual", color = "red") |>
|
||||
e_axis_labels(x = "GL+Cost Center", y = "Deviation") |>
|
||||
#e_title("Plan Vs. Actual") |>
|
||||
e_tooltip() |>
|
||||
e_legend(right = 100) |>
|
||||
e_datazoom(x_index = 0, type = "slider") |>
|
||||
e_datazoom(y_index = 0, type = "slider") |>
|
||||
e_image_g(
|
||||
right = 50,
|
||||
top = 20,
|
||||
z = -999,
|
||||
style = list(
|
||||
image = "logo.png",
|
||||
width = 75,
|
||||
height = 75,
|
||||
opacity = .6
|
||||
)
|
||||
) |>
|
||||
e_toolbox_feature(feature = c("saveAsImage", "dataView")) |>
|
||||
e_theme("roma")
|
||||
})
|
||||
|
||||
output$monthlyabsvar <- renderEcharts4r({
|
||||
req(dat.monthly.per())
|
||||
|
||||
e_chart(dat.monthly.per(), x = month) |>
|
||||
e_bar(serie = devia) |>
|
||||
e_axis_labels(x = "month", y = "Deviation") |>
|
||||
e_format_y_axis(suffix = "€") |>
|
||||
#e_title("Deviation Percentage by month") |>
|
||||
e_tooltip() |>
|
||||
e_legend(right = 100) |>
|
||||
e_datazoom(x_index = c(0, 1)) |>
|
||||
e_image_g(
|
||||
right = 50,
|
||||
top = 20,
|
||||
z = -999,
|
||||
style = list(
|
||||
image = "logo.png",
|
||||
width = 75,
|
||||
height = 75,
|
||||
opacity = .6
|
||||
)
|
||||
) |>
|
||||
e_visual_map(type = "piecewise",
|
||||
pieces = list(list(gt = 0,
|
||||
color = "red", opacity=0.5),
|
||||
list(lte = 0,
|
||||
color = "green", opacity=0.5))) |>
|
||||
e_toolbox_feature(feature = c("saveAsImage", "dataView")) |>
|
||||
e_theme("roma")
|
||||
|
||||
})
|
||||
|
||||
# Table of deviations without explanation
|
||||
|
||||
|
||||
datatableServer("devnoexp", dat.need.exp)
|
||||
|
||||
# Notify
|
||||
|
||||
observeEvent(input$notify, {
|
||||
req(dat.need.exp())
|
||||
emaildf<-dat.need.exp() |>
|
||||
inner_join(emailids(), by=c("Cost.center"="Cost.center","GL.account"="GL.account"))
|
||||
print(emaildf)
|
||||
if(is.null(emaildf) | nrow(emaildf)==0){
|
||||
showModal(modalDialog("No Data"))
|
||||
} else {
|
||||
emails<-
|
||||
emaildf |>
|
||||
select(email) |> pull() |> unique()
|
||||
|
||||
for(i in 1:length(emails)){
|
||||
tbl <- emaildf |>
|
||||
filter(email==emails[i]) |> kbl()
|
||||
date_time <- add_readable_time()
|
||||
email <-
|
||||
compose_email(body = md(c(
|
||||
glue::glue(
|
||||
"Hello,
|
||||
|
||||
Explanation required for expense deviation. Please visit xyz. Details are as follows.
|
||||
|
||||
"
|
||||
),
|
||||
tbl
|
||||
)),
|
||||
footer = md(glue::glue("Email sent on {date_time}.")))
|
||||
|
||||
email |>
|
||||
smtp_send(
|
||||
to = emails[i],
|
||||
from = "asitav.sen@lanubia.com",
|
||||
subject = "Testing",
|
||||
credentials = creds_file("email_creds")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
showModal(modalDialog(title = "Done"))
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
# Approvals
|
||||
output$expnoapp <- renderExcel({
|
||||
req(approvals())
|
||||
appro <- approvals()
|
||||
row.names(appro) <- NULL
|
||||
appro <- appro[appro$approved == FALSE, ]
|
||||
columns = data.frame(
|
||||
title = colnames(appro),
|
||||
type = c('numeric', 'text', 'text', 'text', 'checkbox')
|
||||
)
|
||||
excelTable(data = appro, columns = columns, autoFill = TRUE)
|
||||
|
||||
})
|
||||
|
||||
# Update approvals
|
||||
observeEvent(input$approvalsubmit, {
|
||||
approved.now <- excel_to_R(input$expnoapp)
|
||||
if (is.null(input$expnoapp) || nrow(approved.now) == 0) {
|
||||
showModal(modalDialog(title = "Nothing to Upload"))
|
||||
} else{
|
||||
showModal(modalDialog(title = "Upload in Database?",
|
||||
actionButton("finalapprovalsubmit", "Yes")))
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
observeEvent(input$finalapprovalsubmit, {
|
||||
appro <- approvals()
|
||||
appro <- appro[appro$approved == TRUE, ]
|
||||
approved.now <- excel_to_R(input$expnoapp)
|
||||
appro <- rbind(appro, approved.now)
|
||||
if (nrow(approved.now) > 0) {
|
||||
if (dbWriteTable(connec, "approvals", appro, overwrite = TRUE)) {
|
||||
removeModal()
|
||||
session$reload()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
# New Data
|
||||
fresh.dat <- reactive({
|
||||
req(input$inpfile)
|
||||
file <- input$inpfile
|
||||
ext <- tools::file_ext(file$datapath)
|
||||
req(file)
|
||||
validate(need(ext == "csv", "Please upload a csv file"))
|
||||
new.dat <- read.csv(file$datapath, header = T)
|
||||
new.dat |>
|
||||
mutate(devia = Actual - Plan) |>
|
||||
mutate(devia.per = round(devia / Plan, 2))
|
||||
}, label = "fresh")
|
||||
|
||||
# Show uploaded data
|
||||
datatableServer("freshdat", fresh.dat)
|
||||
|
||||
# Update DB
|
||||
observeEvent(input$datsubmit, {
|
||||
req(nrow(fresh.dat()) > 0)
|
||||
fresh.dt <- fresh.dat()
|
||||
if (is.null(fresh.dt) || nrow(fresh.dt) == 0) {
|
||||
showModal(modalDialog(title = "Nothing to Upload"))
|
||||
} else{
|
||||
showModal(modalDialog(title = "Upload in Database?",
|
||||
actionButton("finaldatsubmit", "Yes")))
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
observeEvent(input$finaldatsubmit, {
|
||||
fresh.dt <- fresh.dat()
|
||||
if (nrow(fresh.dt) > 0) {
|
||||
if (dbWriteTable(connec, "calculated", fresh.dt, append = TRUE)) {
|
||||
unique(fresh.dt$month) |> saveRDS("./data/months.RDS")
|
||||
removeModal()
|
||||
session$reload()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
# Email id edit and show
|
||||
output$emailtable <- renderExcel({
|
||||
req(emailids())
|
||||
email.table <- emailids()
|
||||
columns = data.frame(title = colnames(email.table),
|
||||
type = c('text', 'text', 'text'))
|
||||
excelTable(data = email.table, columns = columns, autoFill = TRUE)
|
||||
|
||||
})
|
||||
|
||||
# Email id table update
|
||||
|
||||
observeEvent(input$emailsubmit, {
|
||||
email.table <- excel_to_R(input$emailtable)
|
||||
if (is.null(input$emailtable) || nrow(email.table) == 0) {
|
||||
showModal(modalDialog(title = "Nothing to Upload"))
|
||||
} else{
|
||||
showModal(modalDialog(title = "Upload in Database?",
|
||||
actionButton("finalemailsubmit", "Yes")))
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
observeEvent(input$finalemailsubmit, {
|
||||
email.table <- excel_to_R(input$emailtable)
|
||||
if (nrow(email.table) > 0) {
|
||||
if (dbWriteTable(connec, "emails", email.table, overwrite = TRUE)) {
|
||||
removeModal()
|
||||
session$reload()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# Limits (deviation rules) id edit and show
|
||||
output$limitable <- renderExcel({
|
||||
req(lims())
|
||||
limit.table <- lims()
|
||||
columns = data.frame(title = colnames(limit.table),
|
||||
type = c('text', 'numeric'))
|
||||
excelTable(data = limit.table, columns = columns, autoFill = TRUE)
|
||||
|
||||
})
|
||||
|
||||
# Email id table update
|
||||
|
||||
observeEvent(input$limitsubmit, {
|
||||
limit.table <- excel_to_R(input$limitable)
|
||||
if (is.null(input$limitable) || nrow(limit.table) == 0) {
|
||||
showModal(modalDialog(title = "Nothing to Upload"))
|
||||
} else{
|
||||
showModal(modalDialog(title = "Upload in Database?",
|
||||
actionButton("finallimitsubmit", "Yes")))
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
observeEvent(input$finallimitsubmit, {
|
||||
limit.table <- excel_to_R(input$limitable)
|
||||
if (nrow(limit.table) > 0) {
|
||||
if (dbWriteTable(connec, "limits", limit.table, overwrite = TRUE)) {
|
||||
removeModal()
|
||||
session$reload()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# User specific explanation filter
|
||||
user.accounts <- reactive({
|
||||
req(dat())
|
||||
req(emailids())
|
||||
req(exp())
|
||||
req(res_auth$email)
|
||||
dat() |>
|
||||
inner_join(lims(), by = c("GL.account")) |>
|
||||
mutate(act.req = ifelse(devia.per > Limit &
|
||||
devia > input$limittoignore, T, F)) |> #change 500 to input
|
||||
filter(act.req) |>
|
||||
left_join(emailids(),
|
||||
by = c("Cost.center" = "Cost.center", "GL.account" = "GL.account")) |>
|
||||
left_join(
|
||||
exp(),
|
||||
by = c(
|
||||
"month" = "month",
|
||||
"Cost.center" = "Cost.center",
|
||||
"GL.account" = "GL.account"
|
||||
)
|
||||
) |>
|
||||
filter(is.na(explanation) | explanation == "") |>
|
||||
filter(email == res_auth$email) |>
|
||||
select(-c(9, 10))
|
||||
})
|
||||
|
||||
output$explanationtofill <- renderExcel({
|
||||
req(user.accounts())
|
||||
explanation.table <- user.accounts()
|
||||
columns = data.frame(
|
||||
title = colnames(explanation.table),
|
||||
type = c(
|
||||
'numeric',
|
||||
'text',
|
||||
'text',
|
||||
'numeric',
|
||||
'numeric',
|
||||
'numeric',
|
||||
'numeric',
|
||||
'numeric',
|
||||
'text'
|
||||
)
|
||||
)
|
||||
excelTable(data = explanation.table, columns = columns, autoFill = TRUE)
|
||||
|
||||
})
|
||||
|
||||
|
||||
# Explanations table update
|
||||
|
||||
observeEvent(input$explanationsubmit, {
|
||||
explanation.table <- excel_to_R(input$explanationtofill)
|
||||
if (is.null(input$explanationtofill) ||
|
||||
nrow(explanation.table) == 0) {
|
||||
showModal(modalDialog(title = "Nothing to Upload"))
|
||||
} else{
|
||||
showModal(modalDialog(title = "Upload in Database?",
|
||||
actionButton("finalexpsubmit", "Yes")))
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
observeEvent(input$finalexpsubmit, {
|
||||
explanation.table <- excel_to_R(input$explanationtofill)
|
||||
explanation.table <- explanation.table |>
|
||||
select(c(month, Cost.center, GL.account, explanation))
|
||||
if (nrow(explanation.table) > 0) {
|
||||
if (dbWriteTable(connec, "explanations", explanation.table, append = TRUE)) {
|
||||
appr <- explanation.table |> mutate(approved = F)
|
||||
if (dbWriteTable(connec, "approvals", appr, append = TRUE)) {
|
||||
removeModal()
|
||||
session$reload()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
125
ui.R
Normal file
125
ui.R
Normal file
@@ -0,0 +1,125 @@
|
||||
#
|
||||
# Custom App developed for Finance Team of Aramco
|
||||
# ::Asitav Sen::
|
||||
# ::LaNubia Consulting::
|
||||
# ::asitav.sen@lanubia.com::
|
||||
#
|
||||
|
||||
library(shiny)
|
||||
library(shinydashboard)
|
||||
library(countup)
|
||||
library(shinyWidgets)
|
||||
library(shinydashboardPlus)
|
||||
library(DBI)
|
||||
library(RPostgres)
|
||||
library(dplyr)
|
||||
library(echarts4r)
|
||||
library(lubridate)
|
||||
library(DT)
|
||||
library(excelR)
|
||||
library(blastula)
|
||||
library(shinymanager)
|
||||
library(glue)
|
||||
library(shinythemes)
|
||||
library(kableExtra)
|
||||
library(waiter)
|
||||
|
||||
|
||||
|
||||
source("helper_server.R")
|
||||
source("mod_valuebox.R")
|
||||
source("mod_elinearea.R")
|
||||
source("mod_step.R")
|
||||
source("mod_datatable.R")
|
||||
source("helper_ui.R")
|
||||
|
||||
# Define UI for application
|
||||
|
||||
shinyUI(
|
||||
secure_app(
|
||||
shinydashboardPlus::dashboardPage(
|
||||
title = "Budgetrack",
|
||||
#skin = "blue-light",
|
||||
#skin = "midnight",
|
||||
header = shinydashboardPlus::dashboardHeader(title = "Budgetrack"),
|
||||
sidebar = shinydashboardPlus::dashboardSidebar(
|
||||
sidebarMenuOutput("menu")
|
||||
),
|
||||
body = dashboardBody(
|
||||
|
||||
tags$head(tags$style(HTML(
|
||||
|
||||
'
|
||||
/* logo */
|
||||
.skin-blue .main-header .logo {
|
||||
background-color: #0477ci;
|
||||
}
|
||||
|
||||
/* logo when hovered */
|
||||
.skin-blue .main-header .logo:hover {
|
||||
background-color: #009adc;
|
||||
}
|
||||
|
||||
/* navbar (rest of the header) */
|
||||
.skin-blue .main-header .navbar {
|
||||
background-color: #0033a0;
|
||||
}
|
||||
|
||||
/* main sidebar */
|
||||
.skin-blue .main-sidebar {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* active selected tab in the sidebarmenu */
|
||||
.skin-blue .main-sidebar .sidebar .sidebar-menu .active a{
|
||||
background-color: #01a54b;
|
||||
}
|
||||
|
||||
/* other links in the sidebarmenu */
|
||||
.skin-blue .main-sidebar .sidebar .sidebar-menu a{
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* other links in the sidebarmenu when hovered */
|
||||
.skin-blue .main-sidebar .sidebar .sidebar-menu a:hover{
|
||||
background-color: #009adc;
|
||||
}
|
||||
'
|
||||
|
||||
))),
|
||||
tabItems(dashboard,
|
||||
upload,
|
||||
appndev,
|
||||
explan,
|
||||
admin))
|
||||
),
|
||||
enable_admin = TRUE,
|
||||
theme = shinythemes::shinytheme("united"),
|
||||
tags_top =
|
||||
tags$div(
|
||||
tags$h4("Created exclusively for ", style ="align:center"),
|
||||
br(),
|
||||
tags$img(
|
||||
src = "https://www.aramco.com/images/affiliateLogo-2x.png", width = 100
|
||||
),
|
||||
br(),
|
||||
br(),
|
||||
tags$h4("By", style ="align:center"),
|
||||
tags$img(src="logo.png", width=100)
|
||||
),
|
||||
tags_bottom = tags$p(
|
||||
"For any question, please contact",
|
||||
tags$a(
|
||||
href ="mailto:asitav.sen@lanubia.com?Subject=Aramco%20aBugdet",
|
||||
target="_top","Asitav Sen"
|
||||
)
|
||||
),
|
||||
background = "linear-gradient(225deg,rgb(0,163,224),
|
||||
rgb(0,51,160),
|
||||
rgb(0,132,61),
|
||||
rgb(132,189,0));"
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
BIN
www/loader.gif
Normal file
BIN
www/loader.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
BIN
www/logo.png
Normal file
BIN
www/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user