We create a suitable environment for performing the required operations on the given data-sets by loading the required libraries before-hand. The data-sets that are required are also loaded during preliminary stage.
rm(list=ls())
library(sqldf)
library(plyr)
library(readxl)
library(stringr)
library(lubridate)
library(benford.analysis)
library(pwr)
library(pps)
We create few functions for enhancing re-usability and efficiency of the project.
importAccounts = function() {
library(readxl, readr)
path = "/Users/Mughundhan/UIC/UIC Academics/SPRING 2017/AUDIT/Final Presentation/Dataset" ## folder for files downloaded from UIC Blackboard
files = c("arConfirmations.csv", "custCredit.csv", "empReimbursements.csv", "inventoryCounts.csv", "inventoryPerpetual.csv", "arCollections.csv", "purchases.csv", "sales.csv")
dataFrameList = list()
for(i in 1:length(files)){
dataFrameName = strsplit(files[i], ".", fixed = TRUE)[[1]][1]
fileType = strsplit(files[i], ".", fixed = TRUE)[[1]][2]
if(fileType == "xlsx") {
dataFrame = read_excel(paste(path, files[i], sep = "/"))
} else {
dataFrame = read.csv(paste(path, files[i], sep = "/"))
}
namedFrame = assign(dataFrameName, dataFrame)
dataFrameList[[dataFrameName]] = namedFrame
}
return(dataFrameList)
}
convertAccounts = function(accounts) {
library(stringr)
library(lubridate)
for(i in 1:length(accounts)) {
for (n in 1:length(accounts[[i]])) {
dataFrame = accounts[[i]]
if(str_detect(names(dataFrame[n]), "date") | str_detect(names(dataFrame[n]), "dateColl")){
if(is.factor(dataFrame[[n]])){
accounts[[i]][[n]] = ymd(as.character(dataFrame[[n]]))
}
}
else if(str_detect(names(dataFrame[n]), "sku") | str_detect(names(dataFrame[n]), "invoice")
| str_detect(names(dataFrame[n]), ".no") | str_detect(names(dataFrame[n]), ".No") | str_detect(names(dataFrame[n]), "customer")){
accounts[[i]][[n]] = as.character(dataFrame[[n]])
}
else if (str_detect(names(dataFrame[n]), "cashtrue")) {
accounts[[i]][[n]] = as.logical(dataFrame[[n]])
}
else if(str_detect(names(dataFrame[n]), "Amount")){
accounts[[i]][[n]] = as.numeric(dataFrame[[n]])
}
}
}
return(accounts)
}
createCostofGoodsSold = function(accounts){
costOfGoodsSold = merge(accounts$sales, accounts$inventoryPerpetual, by="sku", all.x=T)
costOfGoodsSold$COGS = costOfGoodsSold$unitcost * costOfGoodsSold$qty
accounts[["costOfGoodsSold"]] = costOfGoodsSold
return(accounts)
}
createUnpaidAccountsReceivable = function(accounts) {
splitSalesbyTransaction = split(accounts$sales, accounts$sales$cashtrue)
credit = splitSalesbyTransaction[["FALSE"]]
allCreditAccounts = merge(credit, accounts$arCollections, by="invoice", all.x = T)
allCreditAccounts$notCollected = is.na(allCreditAccounts$amt.received)
allCreditAccountsbyCollection = split(allCreditAccounts, allCreditAccounts$notCollect)
unpaidAccountsReceivable = allCreditAccountsbyCollection[["TRUE"]]
accounts[["unpaidAccountsReceivable"]] = unpaidAccountsReceivable
return(accounts)
}
createAllowanceForDoubtfulAccounts = function(accounts) {
x = accounts$unpaidAccountsReceivable
endDateVector = rep(ymd("2016/12/31"), length(accounts$unpaidAccountsReceivable$invoice))
x$endDate = endDateVector
x$daysSincePurchase = x$endDate - x$date
x$interval = findInterval(x$daysSincePurchase, c(90, 180))
accounts[["doubtfulAccounts"]] = x
return(accounts)
}
createOutofStock = function(accounts){
salesBySKU = aggregate(qty~sku, accounts$sales,sum)
purchasesBySKU = aggregate(quantity~sku,accounts$purchases,sum)
purcahsesSalesBySKU = merge(salesBySKU, purchasesBySKU, by="sku")
purchasesSalesInventoryBySKU = merge(purcahsesSalesBySKU, accounts$inventory, by="sku")
purchasesSalesInventoryBySKU$turnover = (purchasesSalesInventoryBySKU$qtypurchasesSalesInventoryBySKU$quantity)/purchasesSalesInventoryBySKU$endstock
turnover = data.frame(purchasesSalesInventoryBySKU$sku,purchasesSalesInventoryBySKU$turnover)
colnames(turnover)=c("sku","times")
accounts[["turnover"]] = turnover
return(accounts)
}
createAccountsByYear = function(accounts, year) {
for(i in 1:length(accounts)) {
for (n in 1:length(accounts[[i]])) {
dataFrame = accounts[[i]]
dateColumnExists = FALSE
if(str_detect(names(dataFrame[n]), "date") | str_detect(names(dataFrame[n]), "dateColl")){
dateColumn = n
dateColumnExists = TRUE
break()
}
}
if(dateColumnExists == TRUE) {
accounts[[i]]$year = year(accounts[[i]][[dateColumn]])
dataFramebyYear = split(accounts[[i]], accounts[[i]][["year"]])
accounts[[i]] = dataFramebyYear[[year]]
}
}
return(accounts)
}
Now, we make use of the above functions to Filter Audit Year-2016’s Transactions and few rows of the Audit Year-2016’s Transactions are displayed below
accounts = importAccounts()
accounts = convertAccounts(accounts)
accounts2016 = createAccountsByYear(accounts, year = "2016")
accounts2016 = createCostofGoodsSold(accounts2016)
accounts2016 = createUnpaidAccountsReceivable(accounts2016)
accounts2016 = createAllowanceForDoubtfulAccounts(accounts2016)
#head(accounts2016)
Now let us have a look at the solutions for the questions posted on Blackboard.
## RISKS IMPACT LIKELIHOOD RISK_FACTOR
## [1,] "Cash" "5" "7" "35"
## [2,] "Accounts Receivable" "8" "9" "72"
## [3,] "Inventory" "6" "8" "48"
## [4,] "Fixed Assets" "8" "4" "32"
## [5,] "Accounts Payable" "3" "7" "21"
## [6,] "Cost of Goods Sold" "8" "5" "40"
## [7,] "Depreciation Expense" "5" "6" "30"
## [8,] "Sales Revenue (net)" "7" "8" "56"
## [9,] "Employee Expenses" "9" "7" "63"
## [10,] "Allowances for Doubtful Accounts" "6" "7" "42"
We also ran a t-test using pwr library and we got the random sample size values for account receivable audit and inventory audit. These are as follows -
## Audits Sizes
## [1,] "Accounts Receivable Audit" "1483718"
## [2,] "Inventory Audit" "185774"
findCreditNegatives = function(accounts) {
library(plyr, dplyr)
#Prepare Sales table
sales = split(accounts$sales, accounts$sales$cashtrue)[["FALSE"]]
sales = subset(sales, select = c(date, cust.no, total))
names(sales)[names(sales) == "total"] = "trans"
sales$trans = sales$trans*-1
#Prepare Collections table
collections = merge(accounts$sales, accounts$arCollections, by = "invoice", all.x = T)
collections = na.omit(collections)
collections = subset(collections, select = c(dateColl, cust.no.x, amt.received))
names(collections)[names(collections) == "dateColl"] = "date"
names(collections)[names(collections) == "amt.received"] = "trans"
names(collections)[names(collections) == "cust.no.x"] = "cust.no"
#TransactionsTable
transTable = rbind(sales, collections)
transTable = arrange(transTable, date)
#Create TransByCustomer
transByCustomer = split(transTable, transTable$cust.no)
#Loop through customers
badCreditAccount = data.frame()
for(i in 1:length(transByCustomer)) {
customer = transByCustomer[[i]]
customerNumber = transByCustomer[[i]][1,]$cust.no
customer$subTotal = accounts$custCredit[as.numeric(customerNumber),]$limit
#loop through customer
for(n in 1:length(customer$subTotal)) {
if(n != 1) {
customer[n,]$subTotal = customer[n - 1,]$subTotal + customer[n,]$trans
if(sign(customer[n,]$subTotal) == -1) {
badCreditAccount = rbind(badCreditAccount, customer[n,])
break
}
}
}
}
accounts[["overlimitCreditApprovals"]] = badCreditAccount
return(accounts)
}
accounts2016 = findCreditNegatives(accounts2016)
#head(accounts2016$overlimitCreditApprovals)
## [1] 485
findDuplicates = function(dataframe, column) {
dataframe$test = as.numeric(dataframe[[column]])
dataframe$dup = duplicated(dataframe$test)
x = split(dataframe, dataframe$dup)
y = x[["TRUE"]]
print(y)
print ("Duplicates (head)")
head(y)
}
findDuplicates(dataframe = accounts2016$sales, column = "invoice")
## NULL
## [1] "Duplicates (head)"
## NULL
findMissingEntries =function(max,set) {
good = 1:max
test = as.numeric(set)
missing = setdiff(good, set)
print(missing)
print ("Missing (head)")
head(missing)
}
#head(findMissingEntries(max = length(accounts2016$sales$invoice), set = accounts2016$sales$invoice))
findSalesNotIn2016 = function(accounts) {
x = accounts$sales
x$year = year(accounts$sales$date)
y = split(x, x$year)
z = rbind(y[["2015"]], y[["2017"]])
print("Transactions not in 2016")
print(z)
print ("Transactions not in 2016 (head)")
head(z)
}
#head(findSalesNotIn2016(accounts))
accountTotals = function(accounts) {
#SALES REVENUE:
print("Sales Revenue")
totalSalesRevenue = sum(accounts$sales$total)
print(totalSalesRevenue)
#SALES RETURNS:
print("Sales Returns")
x = aggregate((returns)*unitprice ~ sku, accounts$inventoryPerpetual, sum)
print(sum(x$`(returns) * unitprice`))
#COGS:
print("COGS")
totalCOGS = sum(accounts$costOfGoodsSold$COGS)
print(totalCOGS)
#ACCOUNTS RECEIVABLE:
print("Accounts Receivable")
totalAR = sum(accounts$unpaidAccountsReceivable$total)
print(sum(accounts$unpaidAccountsReceivable$total))
#COLLECTIONS:
print("Collections")
totalCollections = sum(accounts$arCollections$amt.received)
print(totalCollections)
#INVENTORY:
print("Inventory Perpetual on 1/1/2016")
print(sum(accounts$inventoryPerpetual$beginstock))
print("Inventory Perpetual on 12/31/2016")
print(sum(accounts$inventoryPerpetual$endstock))
print("Inventory Perpetual Cost on 1/1/2016")
beginInventoryValue = sum(accounts$inventoryPerpetual$unitcost*accounts$inventoryPerpetual$beginstock)
print(beginInventoryValue)
print("Inventory Perpetual Cost on 12/31/2016")
endInventoryValue = sum(accounts$inventoryPerpetual$unitcost*accounts$inventoryPerpetual$endstock)
print(endInventoryValue)
#PURCHASES:
print("Purchases Cost")
totalPurchasesCost = sum(accounts$purchases$unitcost*accounts$purchases$quantity)
print(totalPurchasesCost)
#EMPLOYEE REIMBURSEMENTS:
print("Employee Reimbursements total")
totalEmployeeReimbursements = sum(accounts$empReimbursements$Amount)
print(totalEmployeeReimbursements)
}
accountTotals(accounts2016)
## [1] "Sales Revenue"
## [1] 960030574
## [1] "Sales Returns"
## [1] 2014072
## [1] "COGS"
## [1] 350802594
## [1] "Accounts Receivable"
## [1] 333286020
## [1] "Collections"
## [1] 650887909
## [1] "Inventory Perpetual on 1/1/2016"
## [1] 25086639
## [1] "Inventory Perpetual on 12/31/2016"
## [1] 25059323
## [1] "Inventory Perpetual Cost on 1/1/2016"
## [1] 151790200
## [1] "Inventory Perpetual Cost on 12/31/2016"
## [1] 152765109
## [1] "Purchases Cost"
## [1] 418576367
## [1] "Employee Reimbursements total"
## [1] 72750312
## [1] "Foot(total) of Sales"
## [1] 960030574
summarizeAccount = function(accounts) {
for(i in 1:length(accounts)){
print(names(accounts[i]))
print(summary(accounts[[i]]))
}
}
summarizeAccount(accounts2016)
## [1] "arConfirmations"
## X invoice cust.no amt.received
## Min. : 4 Length:411248 Length:411248 Min. : -129.0
## 1st Qu.:229246 Class :character Class :character 1st Qu.: 246.2
## Median :458039 Mode :character Mode :character Median : 629.3
## Mean :458228 Mean : 991.4
## 3rd Qu.:687396 3rd Qu.: 1343.3
## Max. :916833 Max. :15174.1
## [1] "custCredit"
## customer.no limit
## Length:1000 Min. :131000
## Class :character 1st Qu.:268750
## Mode :character Median :278000
## Mean :276868
## 3rd Qu.:286000
## Max. :314000
## [1] "empReimbursements"
## Receipt.No Employee.No Amount
## Length:12428 Length:12428 Min. : 1
## Class :character Class :character 1st Qu.: 2921
## Mode :character Mode :character Median : 5860
## Mean : 5854
## 3rd Qu.: 8781
## Max. :11706
## [1] "inventoryCounts"
## sku defective endstock returns
## Length:2000 Min. : 55.0 Min. : 5005 Min. : 7.0
## Class :character 1st Qu.: 156.8 1st Qu.: 8750 1st Qu.: 25.0
## Mode :character Median : 226.0 Median :12632 Median : 42.0
## Mean : 315.5 Mean :12560 Mean : 62.2
## 3rd Qu.: 387.0 3rd Qu.:16335 3rd Qu.: 75.0
## Max. :1825.0 Max. :20112 Max. :485.0
## [1] "inventoryPerpetual"
## X sku unitcost unitprice
## Min. : 1.0 Length:2000 Min. : 0.000 Min. : 0.000
## 1st Qu.: 500.8 Class :character 1st Qu.: 3.940 1st Qu.: 9.838
## Median :1000.5 Mode :character Median : 5.965 Median :15.095
## Mean :1000.5 Mean : 6.061 Mean :16.572
## 3rd Qu.:1500.2 3rd Qu.: 8.070 3rd Qu.:22.260
## Max. :2000.0 Max. :15.710 Max. :54.160
## beginstock endstock defective returns
## Min. : 5007 Min. : 5002 Min. : 53.0 Min. : 7.0
## 1st Qu.: 8857 1st Qu.: 8719 1st Qu.: 154.8 1st Qu.: 25.0
## Median :12576 Median :12602 Median : 225.0 Median : 41.5
## Mean :12543 Mean :12530 Mean : 313.4 Mean : 61.8
## 3rd Qu.:16218 3rd Qu.:16305 3rd Qu.: 384.0 3rd Qu.: 74.0
## Max. :19996 Max. :20000 Max. :1813.0 Max. :485.0
## [1] "arCollections"
## X invoice cust.no
## Min. : 1 Length:660320 Length:660320
## 1st Qu.: 274908 Class :character Class :character
## Median : 548774 Mode :character Mode :character
## Mean : 549552
## 3rd Qu.: 824492
## Max. :1099998
## dateColl amt.received year
## Min. :2016-01-01 Min. : 0.0 Min. :2016
## 1st Qu.:2016-05-23 1st Qu.: 244.2 1st Qu.:2016
## Median :2016-08-14 Median : 626.6 Median :2016
## Mean :2016-08-04 Mean : 985.7 Mean :2016
## 3rd Qu.:2016-10-25 3rd Qu.: 1338.1 3rd Qu.:2016
## Max. :2016-12-31 Max. :15002.3 Max. :2016
## [1] "purchases"
## X sku unitcost quantity
## Min. : 1 Length:24000 Min. : 0.000 Min. : 976
## 1st Qu.: 6001 Class :character 1st Qu.: 3.940 1st Qu.:2518
## Median :12000 Mode :character Median : 5.965 Median :2884
## Mean :12000 Mean : 6.061 Mean :2887
## 3rd Qu.:18000 3rd Qu.: 8.070 3rd Qu.:3268
## Max. :24000 Max. :15.710 Max. :4215
## date PO.no year
## Min. :2016-01-05 Length:24000 Min. :2016
## 1st Qu.:2016-03-25 Class :character 1st Qu.:2016
## Median :2016-06-17 Mode :character Median :2016
## Mean :2016-06-17 Mean :2016
## 3rd Qu.:2016-09-08 3rd Qu.:2016
## Max. :2016-12-02 Max. :2016
## [1] "sales"
## X invoice sku qty
## Min. : 1 Length:1083467 Length:1083467 Min. : 0.00
## 1st Qu.: 325200 Class :character Class :character 1st Qu.: 15.00
## Median : 650363 Mode :character Mode :character Median : 40.00
## Mean : 650261 Mean : 53.44
## 3rd Qu.: 975510 3rd Qu.: 77.00
## Max. :1300000 Max. :433.00
## cashtrue date unitprice total
## Mode :logical Min. :2016-01-01 Min. : 0.00 Min. : 0.0
## FALSE:916833 1st Qu.:2016-04-01 1st Qu.: 9.84 1st Qu.: 180.6
## TRUE :166634 Median :2016-07-01 Median :15.14 Median : 526.0
## Mean :2016-07-01 Mean :16.58 Mean : 886.1
## 3rd Qu.:2016-10-01 3rd Qu.:22.26 3rd Qu.: 1202.1
## Max. :2016-12-31 Max. :54.16 Max. :15174.1
## cust.no year
## Length:1083467 Min. :2016
## Class :character 1st Qu.:2016
## Mode :character Median :2016
## Mean :2016
## 3rd Qu.:2016
## Max. :2016
## [1] "costOfGoodsSold"
## sku X.x invoice qty
## Length:1083467 Min. : 1 Length:1083467 Min. : 0.00
## Class :character 1st Qu.: 325200 Class :character 1st Qu.: 15.00
## Mode :character Median : 650363 Mode :character Median : 40.00
## Mean : 650261 Mean : 53.44
## 3rd Qu.: 975510 3rd Qu.: 77.00
## Max. :1300000 Max. :433.00
## cashtrue date unitprice.x total
## Mode :logical Min. :2016-01-01 Min. : 0.00 Min. : 0.0
## FALSE:916833 1st Qu.:2016-04-01 1st Qu.: 9.84 1st Qu.: 180.6
## TRUE :166634 Median :2016-07-01 Median :15.14 Median : 526.0
## Mean :2016-07-01 Mean :16.58 Mean : 886.1
## 3rd Qu.:2016-10-01 3rd Qu.:22.26 3rd Qu.: 1202.1
## Max. :2016-12-31 Max. :54.16 Max. :15174.1
## cust.no year X.y unitcost
## Length:1083467 Min. :2016 Min. : 1 Min. : 0.000
## Class :character 1st Qu.:2016 1st Qu.: 501 1st Qu.: 3.940
## Mode :character Median :2016 Median :1001 Median : 5.960
## Mean :2016 Mean :1001 Mean : 6.061
## 3rd Qu.:2016 3rd Qu.:1500 3rd Qu.: 8.070
## Max. :2016 Max. :2000 Max. :15.710
## unitprice.y beginstock endstock defective
## Min. : 0.00 Min. : 5007 Min. : 5002 Min. : 53.0
## 1st Qu.: 9.84 1st Qu.: 8858 1st Qu.: 8722 1st Qu.: 155.0
## Median :15.14 Median :12575 Median :12603 Median : 225.0
## Mean :16.58 Mean :12544 Mean :12529 Mean : 313.7
## 3rd Qu.:22.26 3rd Qu.:16217 3rd Qu.:16304 3rd Qu.: 385.0
## Max. :54.16 Max. :19996 Max. :20000 Max. :1813.0
## returns COGS
## Min. : 7.00 Min. : 0.00
## 1st Qu.: 25.00 1st Qu.: 69.85
## Median : 42.00 Median : 201.96
## Mean : 61.87 Mean : 323.78
## 3rd Qu.: 74.00 3rd Qu.: 449.48
## Max. :485.00 Max. :5022.50
## [1] "unpaidAccountsReceivable"
## invoice X.x sku qty
## Length:337361 Min. : 17 Length:337361 Min. : 0.00
## Class :character 1st Qu.: 325013 Class :character 1st Qu.: 21.00
## Mode :character Median : 652251 Mode :character Median : 47.00
## Mean : 650967 Mean : 59.59
## 3rd Qu.: 976766 3rd Qu.: 85.00
## Max. :1300000 Max. :433.00
##
## cashtrue date unitprice total
## Mode :logical Min. :2016-01-01 Min. : 0.00 Min. : 0.0
## FALSE:337361 1st Qu.:2016-08-04 1st Qu.: 9.83 1st Qu.: 244.4
## Median :2016-10-10 Median :15.10 Median : 627.7
## Mean :2016-09-20 Mean :16.57 Mean : 987.9
## 3rd Qu.:2016-11-25 3rd Qu.:22.26 3rd Qu.: 1339.7
## Max. :2016-12-31 Max. :54.16 Max. :15174.1
##
## cust.no.x year.x X.y cust.no.y
## Length:337361 Min. :2016 Min. : NA Length:337361
## Class :character 1st Qu.:2016 1st Qu.: NA Class :character
## Mode :character Median :2016 Median : NA Mode :character
## Mean :2016 Mean :NaN
## 3rd Qu.:2016 3rd Qu.: NA
## Max. :2016 Max. : NA
## NA's :337361
## dateColl amt.received year.y notCollected
## Min. :NA Min. : NA Min. : NA Mode:logical
## 1st Qu.:NA 1st Qu.: NA 1st Qu.: NA TRUE:337361
## Median :NA Median : NA Median : NA
## Mean :NA Mean :NaN Mean :NaN
## 3rd Qu.:NA 3rd Qu.: NA 3rd Qu.: NA
## Max. :NA Max. : NA Max. : NA
## NA's :337361 NA's :337361 NA's :337361
## [1] "doubtfulAccounts"
## invoice X.x sku qty
## Length:337361 Min. : 17 Length:337361 Min. : 0.00
## Class :character 1st Qu.: 325013 Class :character 1st Qu.: 21.00
## Mode :character Median : 652251 Mode :character Median : 47.00
## Mean : 650967 Mean : 59.59
## 3rd Qu.: 976766 3rd Qu.: 85.00
## Max. :1300000 Max. :433.00
##
## cashtrue date unitprice total
## Mode :logical Min. :2016-01-01 Min. : 0.00 Min. : 0.0
## FALSE:337361 1st Qu.:2016-08-04 1st Qu.: 9.83 1st Qu.: 244.4
## Median :2016-10-10 Median :15.10 Median : 627.7
## Mean :2016-09-20 Mean :16.57 Mean : 987.9
## 3rd Qu.:2016-11-25 3rd Qu.:22.26 3rd Qu.: 1339.7
## Max. :2016-12-31 Max. :54.16 Max. :15174.1
##
## cust.no.x year.x X.y cust.no.y
## Length:337361 Min. :2016 Min. : NA Length:337361
## Class :character 1st Qu.:2016 1st Qu.: NA Class :character
## Mode :character Median :2016 Median : NA Mode :character
## Mean :2016 Mean :NaN
## 3rd Qu.:2016 3rd Qu.: NA
## Max. :2016 Max. : NA
## NA's :337361
## dateColl amt.received year.y notCollected
## Min. :NA Min. : NA Min. : NA Mode:logical
## 1st Qu.:NA 1st Qu.: NA 1st Qu.: NA TRUE:337361
## Median :NA Median : NA Median : NA
## Mean :NA Mean :NaN Mean :NaN
## 3rd Qu.:NA 3rd Qu.: NA 3rd Qu.: NA
## Max. :NA Max. : NA Max. : NA
## NA's :337361 NA's :337361 NA's :337361
## endDate daysSincePurchase interval
## Min. :2016-12-31 Length:337361 Min. :0.0000
## 1st Qu.:2016-12-31 Class :difftime 1st Qu.:0.0000
## Median :2016-12-31 Mode :numeric Median :0.0000
## Mean :2016-12-31 Mean :0.6416
## 3rd Qu.:2016-12-31 3rd Qu.:1.0000
## Max. :2016-12-31 Max. :2.0000
##
## [1] "overlimitCreditApprovals"
## date cust.no trans
## Min. :2016-04-13 Length:1000 Min. :-11475.32
## 1st Qu.:2016-06-07 Class :character 1st Qu.: -3626.09
## Median :2016-06-28 Mode :character Median : -2202.72
## Mean :2016-07-02 Mean : -2629.86
## 3rd Qu.:2016-07-23 3rd Qu.: -1213.84
## Max. :2016-11-24 Max. : -55.32
## subTotal
## Min. :-8736.986
## 1st Qu.:-1487.470
## Median : -726.845
## Mean :-1123.257
## 3rd Qu.: -302.742
## Max. : -0.782
createDailySales = function(accounts) {
totalSales = accounts$sales
totalSales$amt = totalSales$qty * totalSales$unitprice
dailySales = aggregate(amt~date,totalSales,sum)
accounts[["dailySales"]] = dailySales
return(accounts)
}
createDailyPurchases = function(accounts) {
totalPurchases = accounts$purchases
totalPurchases$amt = totalPurchases$quantity * totalPurchases$unitcost
dailyPurchases = aggregate(amt~date,totalPurchases,sum)
accounts[["dailyPurchases"]] = dailyPurchases
return(accounts)
}
createDailyCollections= function(accounts) {
totalCollections = accounts$arCollections
dailyCollections = aggregate(amt.received~dateColl,totalCollections,sum)
accounts[["dailyCollected"]] = dailyCollections
return(accounts)
}
The above questions shall be solved in a simple way by calling the built-in R functions along with the reusable functions which we created. Since both the questions involves a similar approach, we are going to make use of an unified approach to solve the same (as shown below):
accounts2016 = createDailySales(accounts2016)
summary(accounts2016$dailySales)
## date amt
## Min. :2016-01-01 Min. :1758475
## 1st Qu.:2016-04-01 1st Qu.:2022356
## Median :2016-07-01 Median :2840832
## Mean :2016-07-01 Mean :2623034
## 3rd Qu.:2016-09-30 3rd Qu.:2912291
## Max. :2016-12-31 Max. :3098749
accounts2016 = createDailyPurchases(accounts2016)
summary(accounts2016$dailyPurchases)
## date amt
## Min. :2016-01-05 Min. :34881364
## 1st Qu.:2016-03-25 1st Qu.:34881364
## Median :2016-06-17 Median :34881364
## Mean :2016-06-17 Mean :34881364
## 3rd Qu.:2016-09-08 3rd Qu.:34881364
## Max. :2016-12-02 Max. :34881364
accounts2016 = createDailyCollections(accounts2016)
summary(accounts2016$dailyCollected)
## dateColl amt.received
## Min. :2016-01-01 Min. : 355863
## 1st Qu.:2016-04-01 1st Qu.:1360810
## Median :2016-07-01 Median :1937318
## Mean :2016-07-01 Mean :1778382
## 3rd Qu.:2016-09-30 3rd Qu.:2292952
## Max. :2016-12-31 Max. :2555145
From the above, we shall infer that the Range falls within the fiscal year only if filtered data is passed else it doesnot happen.
If the range doesnot fall in the audit year, then apply year filter using lubridate feature
Computed accounts would not change unless the non filtered data set is used.
#Benford test
accounts2016$empReimbursements$Employee.No = as.integer(accounts2016$empReimbursements$Employee.No)
accounts2016$empReimbursements$Receipt.No = as.integer(accounts2016$empReimbursements$Receipt.No)
auditEmployeeReim = function(accounts) {
amtPerEmployee = aggregate(accounts$empReimbursements$Amount, by = list(accounts$empReimbursements$Employee.No), sum)
names(amtPerEmployee)[names(amtPerEmployee) == "Group.1"] = "employeeNumber"
names(amtPerEmployee)[names(amtPerEmployee) == "x"] = "Amount"
employeeAmt50000 = amtPerEmployee[which(amtPerEmployee$Amount>=50000),]
accounts[["employeeAmt50000"]] = employeeAmt50000
return(accounts)
}
accounts2016 = auditEmployeeReim(accounts2016)
print(head(accounts2016$employeeAmt50000))
## employeeNumber Amount
## 1 0 719370
## 2 1 713562
## 3 2 630122
## 4 3 735776
## 5 4 740818
## 6 5 745801
#Amount
benford_Emp_amount <- benford(accounts2016$empReimbursements$Amount,number.of.digits = 1, sign = "both", round = 3 )
benford_Emp_amount
##
## Benford object:
##
## Data: accounts2016$empReimbursements$Amount
## Number of observations used = 12428
## Number of obs. for second order = 11705
## First digits analysed = 1
##
## Mantissa:
##
## Statistic Value
## Mean 0.58
## Var 0.11
## Ex.Kurtosis -1.10
## Skewness -0.54
##
##
## The 5 largest deviations:
##
## digits absolute.diff
## 1 2 1003.46
## 2 1 764.20
## 3 9 624.33
## 4 8 546.28
## 5 7 468.28
##
## Stats:
##
## Pearson's Chi-squared test
##
## data: accounts2016$empReimbursements$Amount
## X-squared = 2345.9, df = 8, p-value < 2.2e-16
##
##
## Mantissa Arc Test
##
## data: accounts2016$empReimbursements$Amount
## L2 = 0.11869, df = 2, p-value < 2.2e-16
##
## Mean Absolute Deviation: 0.03892285
## Distortion Factor: 24.47812
##
## Remember: Real data will never conform perfectly to Benford's Law. You should not focus on p-values!
plot(benford_Emp_amount)
suspects_amount <- getSuspects(benford_Emp_amount, accounts2016$empReimbursement, how.many=2)
suspects_amount
## Receipt.No Employee.No Amount
## 1: 3550 57 1283
## 2: 3551 27 11485
## 3: 3552 23 10400
## 4: 3555 28 10518
## 5: 3558 64 1259
## ---
## 4158: 15951 39 1915
## 4159: 15953 89 1309
## 4160: 15958 8 1017
## 4161: 15960 1 2848
## 4162: 15962 56 1015
Plots are included in-order to enhance the understandability of the client.
#Employee Number
benford_Emp_EmpNo <- benford(accounts2016$empReimbursements$Employee.No,number.of.digits = 1, sign = "both", round = 3 )
benford_Emp_EmpNo
##
## Benford object:
##
## Data: accounts2016$empReimbursements$Employee.No
## Number of observations used = 12302
## Number of obs. for second order = 98
## First digits analysed = 1
##
## Mantissa:
##
## Statistic Value
## Mean 0.667
## Var 0.068
## Ex.Kurtosis -0.242
## Skewness -0.809
##
##
## The 5 largest deviations:
##
## digits absolute.diff
## 1 1 2362.27
## 2 2 819.27
## 3 9 784.09
## 4 8 720.72
## 5 7 665.58
##
## Stats:
##
## Pearson's Chi-squared test
##
## data: accounts2016$empReimbursements$Employee.No
## X-squared = 4902.7, df = 8, p-value < 2.2e-16
##
##
## Mantissa Arc Test
##
## data: accounts2016$empReimbursements$Employee.No
## L2 = 0.12099, df = 2, p-value < 2.2e-16
##
## Mean Absolute Deviation: 0.05962069
## Distortion Factor: 39.07338
##
## Remember: Real data will never conform perfectly to Benford's Law. You should not focus on p-values!
plot(benford_Emp_EmpNo)
suspects_employee <- getSuspects(benford_Emp_EmpNo, accounts2016$empReimbursement, how.many=2)
suspects_employee
## Receipt.No Employee.No Amount
## 1: 3542 26 4131
## 2: 3551 27 11485
## 3: 3552 23 10400
## 4: 3554 13 5172
## 5: 3555 28 10518
## ---
## 2684: 15930 18 11517
## 2685: 15935 2 4484
## 2686: 15955 20 5731
## 2687: 15960 1 2848
## 2688: 15966 20 6993
#Receipts
benford_Emp_Receipts <- benford(accounts2016$empReimbursements$Receipt.No,number.of.digits = 1, sign = "both", round = 3 )
benford_Emp_Receipts
##
## Benford object:
##
## Data: accounts2016$empReimbursements$Receipt.No
## Number of observations used = 12428
## Number of obs. for second order = 12427
## First digits analysed = 1
##
## Mantissa:
##
## Statistic Value
## Mean 0.475
## Var 0.133
## Ex.Kurtosis -1.756
## Skewness 0.058
##
##
## The 5 largest deviations:
##
## digits absolute.diff
## 1 1 2227.80
## 2 2 2188.46
## 3 3 1093.74
## 4 9 431.33
## 5 8 364.28
##
## Stats:
##
## Pearson's Chi-squared test
##
## data: accounts2016$empReimbursements$Receipt.No
## X-squared = 4998.5, df = 8, p-value < 2.2e-16
##
##
## Mantissa Arc Test
##
## data: accounts2016$empReimbursements$Receipt.No
## L2 = 0.25415, df = 2, p-value < 2.2e-16
##
## Mean Absolute Deviation: 0.06234307
## Distortion Factor: 5.974498
##
## Remember: Real data will never conform perfectly to Benford's Law. You should not focus on p-values!
plot(benford_Emp_Receipts)
Suspicious findings are reported below:
suspects <- getSuspects(benford_Emp_amount, accounts2016$empReimbursement, how.many=2)
suspects
## Receipt.No Employee.No Amount
## 1: 3550 57 1283
## 2: 3551 27 11485
## 3: 3552 23 10400
## 4: 3555 28 10518
## 5: 3558 64 1259
## ---
## 4158: 15951 39 1915
## 4159: 15953 89 1309
## 4160: 15958 8 1017
## 4161: 15960 1 2848
## 4162: 15962 56 1015
print("Unpaid Accounts Receivable")
## [1] "Unpaid Accounts Receivable"
totalAR = sum(accounts2016$unpaidAccountsReceivable$total)
print(sum(accounts2016$unpaidAccountsReceivable$total))
## [1] 333286020
print("Uncollected Accounts Receivable")
## [1] "Uncollected Accounts Receivable"
accounts2016 = createUnpaidAccountsReceivable(accounts2016)
print(sum(accounts2016$unpaidAccountsReceivable$total))
## [1] 333286020
print("Allowance for Doubtful Accounts")
## [1] "Allowance for Doubtful Accounts"
accounts2016 = createAllowanceForDoubtfulAccounts(accounts2016)
doubtfulTotals = aggregate(total~interval, accounts2016$doubtfulAccounts, sum)
print(0.3*doubtfulTotals$total[2] + 0.5*doubtfulTotals$total[3])
## [1] 58398058
findSalesNotIn2016 = function(accounts) {
x = accounts$sales
x$year = year(accounts$sales$date)
y = split(x, x$year)
z = rbind(y[["2015"]], y[["2017"]])
print("Transactions not in 2016")
print(z)
print ("Transactions not in 2016 (head)")
head(z)
}
#head(findSalesNotIn2016(accounts))
d=1000000/333286020
library(pwr)
pwr.t.test (n = NULL, d = 0.003, sig.level = 0.05, power = 0.8, type = "one.sample")
##
## One-sample t test power calculation
##
## n = 872097.5
## d = 0.003
## sig.level = 0.05
## power = 0.8
## alternative = two.sided
mergeSalesAndARConfirmations = function(accounts) {
allARAccounts = merge(accounts$arCollections, accounts$arConfirmations, by="invoice", all.x = T)
allARAccounts = subset(allARAccounts, select = c(invoice, amt.received.x, amt.received.y))
allARAccounts = na.omit(allARAccounts)
accounts[["allARConfirmationsAndCollections"]] = allARAccounts
return(accounts)
}
accounts2016 = mergeSalesAndARConfirmations(accounts2016)
The Percentage Error is given below:
sampleConfirmation = accounts2016$allARConfirmationsAndCollections[ppss(accounts2016$allARConfirmationsAndCollection$amt.received.y, 1483718),]
distinctSampleConfirmation = unique(sampleConfirmation)
difference = sum(distinctSampleConfirmation$amt.received.y - distinctSampleConfirmation$amt.received.x)
totalConfirmedAmounts = sum(distinctSampleConfirmation$amt.received.y)
percentageError = (difference/totalConfirmedAmounts)*100
percentageError
## [1] 0.02955816
The total cost of Goods sold is accounted for the year 2016 and is given below:
accounts2016 = createCostofGoodsSold(accounts2016)
sum(accounts2016$costOfGoodsSold$COGS)
## [1] 350802594
The detailed summary of the MarkUp percentages (Max, Min, Quartiles) are computed and shown below:
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.503 1.078 1.745 1.739 2.374 3.000 11004
findOutOfStockDemand = function(accounts) {
library(plyr)
#prepare tables
sales = subset(accounts$sales, select = c(sku, date, qty))
sales$qty = sales$qty*-1
purchases = accounts$purchases
purchases$qty = purchases$quantity
purchases = subset(purchases, select = c(sku, date, qty))
inventoryTrans = rbind(sales, purchases)
inventoryTrans = arrange(inventoryTrans, date)
#Create dataframe by sku
inventoryTransBySku = split(inventoryTrans, inventoryTrans$sku)
stockOutSkus = list()
for(i in 1:length(inventoryTransBySku)) {
sku = inventoryTransBySku[[i]]
skuNumber = as.numeric(sku[1,]$sku)
sku$onHand = accounts$inventoryPerpetual[skuNumber,]$beginstock
for(n in 1:length(sku$qty)) {
if(n == 1) {
sku[n,]$onHand = sku[n,]$onHand + sku[n,]$qty
}
else {
sku[n,]$onHand = sku[n-1,]$onHand + sku[n,]$qty
}
}
if(sum(sku$onHand < 0) > 0) {
stockOutSkus[[length(stockOutSkus) + 1]] = skuNumber
}
inventoryTransBySku[[i]] = sku
}
stockOutTrans = data.frame()
for(i in 1:length(stockOutSkus)){
skuNumber = stockOutSkus[[i]]
sku = inventoryTransBySku[[as.character(skuNumber)]]
times = which(diff(sign(sku$onHand)) > 0)
for(n in 1:length(times)) {
stockOutTrans = rbind(stockOutTrans, sku[times[n],])
}
}
accounts[["stockOutTrans"]] = stockOutTrans
return(accounts)
}
accounts2016 = findOutOfStockDemand(accounts2016)
accounts2016$stockOutTrans = na.omit(accounts2016$stockOutTrans)
head(accounts2016$stockOutTrans$sku)
## [1] "1084" "1084" "1095" "1124" "1230" "1230"
d=1000000/152765109
d
## [1] 0.006545997
library(pwr)
pwr.t.test (n = NULL, d = 0.0065, sig.level = 0.05, power = 0.8, type = "one.sample")
##
## One-sample t test power calculation
##
## n = 185773.8
## d = 0.0065
## sig.level = 0.05
## power = 0.8
## alternative = two.sided
mergeInventoryPerpetualAndCounts = function(accounts) {
allInventory = merge(accounts$inventoryPerpetual, accounts$inventoryCounts, by="sku", all.x = T)
allInventory = subset(allInventory, select = c(sku, beginstock,endstock.x, endstock.y,unitcost,defective.y,returns.y))
allInventory = na.omit(allInventory)
accounts[["allInventoryMatched"]] = allInventory
return(accounts)
}
accounts2016 = mergeInventoryPerpetualAndCounts(accounts2016)
The Percentage Error is computed and displayed below:
sampleConfirmation = accounts2016$allInventoryMatched[ppss(accounts2016$allInventoryMatched$endstock.y, 185774),]
distinctSampleConfirmation = unique(sampleConfirmation)
sum(distinctSampleConfirmation$endstock.x)
## [1] 25059323
difference = sum(distinctSampleConfirmation$endstock.y - distinctSampleConfirmation$endstock.x)
totalConfirmedAmounts = sum(distinctSampleConfirmation$endstock.y)
percentageError = (difference/totalConfirmedAmounts)*100
percentageError
## [1] 0.241898
The inventory is overstatied by 0.24 % and this would impact the balance sheet. But, this would impact only to a minimal extent.
totalInventoryBalanceAfterAdjusting = sum(accounts2016$allInventoryMatched$endstock.y*accounts2016$allInventoryMatched$unitcost)
totalInventoryBalanceAfterAdjusting
## [1] 153129104
The aged inventory total is computed and given as follows:
createInventoryAgeingData = function(accounts){
inventoryAgeing = merge(accounts$sales, accounts$allInventoryMatched, by="sku", all.x=T)
inventoryAgeing = subset(inventoryAgeing, select = c(sku, date, qty,unitcost,beginstock,endstock.y,total))
inventoryAgeing$COGS = inventoryAgeing$unitcost * inventoryAgeing$qty
inventoryAgeing$AvgInvCost = ((inventoryAgeing$endstock.y + inventoryAgeing$beginstock)* inventoryAgeing$unitcost / 2)
inventoryAgeing$turnover = inventoryAgeing$COGS/inventoryAgeing$AvgInvCost
accounts[["inventoryAgeing"]] = inventoryAgeing
return(accounts)
}
accounts2016 = createInventoryAgeingData(accounts2016)
names(accounts2016$inventoryAgeing)[names(accounts2016$inventoryAgeing) == "endstock.y"] = "endstock"
createInventoryAgeingFinal = function(accounts){
accountsInventoryAgeingSorted=accounts$inventoryAgeing
accountsInventoryAgeingSortedFiltered = sqldf("Select sku, sum(qty) as qty,unitcost,endstock,AvgInvCost from accountsInventoryAgeingSorted group by sku")
accountsInventoryAgeingSortedFiltered$COGS = accountsInventoryAgeingSortedFiltered$qty*accountsInventoryAgeingSortedFiltered$unitcost
accountsInventoryAgeingSortedFiltered$turnOverRatio = accountsInventoryAgeingSortedFiltered$COGS/accountsInventoryAgeingSortedFiltered$AvgInvCost
#accountsInventoryAgeingSortedFiltered = accountsInventoryAgeingSortedFiltered[!(accountsInventoryAgeingSortedFiltered$turnOverRatio==0),]
accountsInventoryAgeingSortedFiltered$age = 365 / accountsInventoryAgeingSortedFiltered$turnOverRatio
accounts[["inventoryAgeingFinal"]] = accountsInventoryAgeingSortedFiltered
return(accounts)
}
accounts2016 = createInventoryAgeingFinal(accounts2016)
accounts2016_backup = accounts2016
#head(accounts2016$inventoryAgeingFinal)
effectiveCostUnderSixty=0
effectiveCostOverSixtyLessOneEighty=0
effectiveCostOver180Less365=0
effectiveCostOver365=0
i=as.integer()
accounts2016$inventoryAgeingFinal$age = as.numeric(accounts2016$inventoryAgeingFinal$age)
#na.omit(accounts2016$inventoryAgeingFinal)
inventoryAgeingCheckData = accounts2016$inventoryAgeingFinal
#inventoryAgeingCheckData[complete.cases(inventoryAgeingCheckData),]
for (i in 1:2000){
#print(i)
#print(accounts2016$inventoryAgeingFinal$age[i])
if(is.na(accounts2016$inventoryAgeingFinal$age[i])){
next
}
if(accounts2016$inventoryAgeingFinal$age[i] < 60){
effectiveCostUnderSixty = effectiveCostUnderSixty + (accounts2016$inventoryAgeingFinal$unitcost[i]*accounts2016$inventoryAgeingFinal$endstock[i])
}else
if(accounts2016$inventoryAgeingFinal$age[i]>=60 && accounts2016$inventoryAgeingFinal$age[i]<180){
effectiveCostOverSixtyLessOneEighty = effectiveCostOverSixtyLessOneEighty + (0.50)*(accounts2016$inventoryAgeingFinal$unitcost[i]*accounts2016$inventoryAgeingFinal$endstock[i])
}else
if(accounts2016$inventoryAgeingFinal$age[i]>=180 && accounts2016$inventoryAgeingFinal$age[i]<365){
effectiveCostOver180Less365 = effectiveCostOver180Less365 + (accounts2016$inventoryAgeingFinal$unitcost[i]*accounts2016$inventoryAgeingFinal$endstock[i])
}else{
effectiveCostOver365 = effectiveCostOver365 + (accounts2016$inventoryAgeingFinal$unitcost[i]*accounts2016$inventoryAgeingFinal$endstock[i])
}
}
agedInventoryTotal = effectiveCostUnderSixty + effectiveCostOverSixtyLessOneEighty + effectiveCostOver180Less365 + effectiveCostOver365
agedInventoryTotal
## [1] 106273976
The computed value for effectiveCostUnderSixty is given below:
effectiveCostUnderSixty
## [1] 0
The computed value for effectiveCostOverSixtyLessOneEighty is given below:
effectiveCostOverSixtyLessOneEighty
## [1] 46855128
The computed value for effectiveCostOver180Less365 is given below:
effectiveCostOver180Less365
## [1] 59418847
The computed value for effectiveCostOver365 is given below:
effectiveCostOver365
## [1] 0
The Percentage of total less than 60
percentageOfTotalLess60 = (effectiveCostUnderSixty/agedInventoryTotal)*100
percentageOfTotalLess60
## [1] 0
The Percentage of total computed for the range between 60 and 180
percentageOfTotalOver60Less180 = (effectiveCostOverSixtyLessOneEighty/agedInventoryTotal)*100
percentageOfTotalOver60Less180
## [1] 44.089
The Percentage of total computed for the range between 180 and 365
percentageOfTotalOver180Less365 = (effectiveCostOver180Less365/agedInventoryTotal)*100
percentageOfTotalOver180Less365
## [1] 55.911
The Percentage of total computed for the range above 365
percentageOfTotalOver365 = (effectiveCostOver365/agedInventoryTotal)*100
percentageOfTotalOver365
## [1] 0
counter=0
for(i in 1:2000){
if(is.na(accounts2016$inventoryAgeingFinal$COGS[i])){
print("NA")
print(i)
print("NA")
next
}
if((accounts2016$inventoryAgeingFinal$COGS[i]/accounts2016$inventoryAgeingFinal$endstock[i]) < 10){
print(accounts2016$inventoryAgeingFinal$sku[i])
counter=counter+1
}
}
counter
## [1] 629
marketTestInventory = function(accounts)
{
inventoryPerpMarketTest = subset(accounts$inventoryPerpetual, select = c(sku, unitprice, unitcost))
InventoryMarketTest = merge(accounts$inventoryCounts,inventoryPerpMarketTest,by="sku")
InventoryMarketTest$diff = (InventoryMarketTest$unitprice-InventoryMarketTest$unitcost) * InventoryMarketTest$endstock
accounts[["InventoryMarketTest"]] = InventoryMarketTest
return(accounts)
}
accounts2016 = marketTestInventory(accounts2016)
#print(head(accounts2016$InventoryMarketTest[InventoryMarketTest$diff < 0,]))
#NULL
salesInventoryMerge = merge(accounts2016$sales,accounts2016$allInventoryMatched,by="sku")
aggregateQuantity=aggregate(salesInventoryMerge$qty,by=list(salesInventoryMerge$sku),sum)
names(aggregateQuantity)[names(aggregateQuantity) == "Group.1"] = "sku"
names(aggregateQuantity)[names(aggregateQuantity) == "x"] = "qty"
head(salesInventoryMerge)
## sku X invoice qty cashtrue date unitprice total cust.no year
## 1 1 505903 505903 4 TRUE 2016-10-12 5.7 22.8 373 2016
## 2 1 278696 278696 122 FALSE 2016-08-02 5.7 695.4 606 2016
## 3 1 962588 962588 12 FALSE 2016-07-22 5.7 68.4 106 2016
## 4 1 454907 454907 2 FALSE 2016-05-24 5.7 11.4 882 2016
## 5 1 688592 688592 39 FALSE 2016-12-25 5.7 222.3 427 2016
## 6 1 917373 917373 104 FALSE 2016-06-18 5.7 592.8 527 2016
## beginstock endstock.x endstock.y unitcost defective.y returns.y
## 1 6714 12175 12344 3.73 100 12
## 2 6714 12175 12344 3.73 100 12
## 3 6714 12175 12344 3.73 100 12
## 4 6714 12175 12344 3.73 100 12
## 5 6714 12175 12344 3.73 100 12
## 6 6714 12175 12344 3.73 100 12
head(aggregateQuantity)
## sku qty
## 1 1 14338
## 2 10 30161
## 3 100 25475
## 4 1000 29117
## 5 1001 28488
## 6 1002 27687
salesInventoryMerge = merge(salesInventoryMerge[,c('sku','unitprice','unitcost','beginstock','endstock.y')],aggregateQuantity,by="sku")
salesInventoryMerge=unique((salesInventoryMerge))
head(salesInventoryMerge)
## sku unitprice unitcost beginstock endstock.y qty
## 1 1 5.70 3.73 6714 12344 14338
## 283 10 3.32 1.88 13325 11346 30161
## 838 100 19.00 8.07 5341 9374 25475
## 1358 1000 17.23 8.29 17136 16128 29117
## 1912 1001 21.77 5.62 16363 8068 28488
## 2422 1002 23.19 8.78 19098 10995 27687
counter=0
for(i in 1:2000){
if(is.na(salesInventoryMerge$unitprice[i]) | is.na(salesInventoryMerge$unitcost[i])){
print("NA")
print(i)
print("NA")
next
}
if((salesInventoryMerge$unitprice[i]-(salesInventoryMerge$unitcost[i])) < salesInventoryMerge$unitcost[i]){
print(salesInventoryMerge$sku[i])
counter=counter+1
}
}
counter
## [1] 433
counter=0
for(i in 1:2000){
if(is.na(salesInventoryMerge$unitprice[i]) | is.na(salesInventoryMerge$unitcost[i])){
print("NA")
print(i)
print("NA")
next
}
if((salesInventoryMerge$unitprice[i] - salesInventoryMerge$unitcost[i] - (0.1 * salesInventoryMerge$unitcost[i])) < (1.1 * salesInventoryMerge$unitcost[i])){
print(salesInventoryMerge$sku[i])
counter=counter+1
}
}
counter
## [1] 587
Notes for Questions 5 - Part 9 and 10
If this calculation does result in a loss, you should charge the loss to the cost of goods sold expense with a debit, and credit the #inventory account to reduce the value of the inventory account. If the loss is material, you may want to segregate it in a separate #loss account, which more easily draws the attention of a reader of a company’s financial statements.
Net realizable value is actually only one of the factors you consider in determining the lower of cost or market, so see the Lower of #Cost or Market article for a complete explanation.
Net realizable value can also refer to the aggregate total of the ending balances in the trade accounts receivable account and the #offsetting allowance for doubtful accounts. This net amount represents the amount of cash that management expects to realize once it #collects all outstanding accounts receivable.
purchasePerSKU = arrange(accounts2016$purchases,accounts2016$purchases$sku)
purchasePerSKU = subset(purchasePerSKU, select = c(sku, quantity))
purchasePerSKU = aggregate(purchasePerSKU$quantity,by=list(purchasePerSKU$sku),sum)
names(purchasePerSKU)[names(purchasePerSKU) == "Group.1"] = "sku"
names(purchasePerSKU)[names(purchasePerSKU) == "x"] = "quantity"
#purchasePerSKU
mergedPurchaseAndInventory = merge(purchasePerSKU,accounts2016$allInventoryMatched,by="sku")
mergedPurchaseAndInventory = subset(mergedPurchaseAndInventory, select = c(sku,quantity,beginstock,returns.y,defective.y))
mergedPurchaseAndInventory$defectiveRate = (mergedPurchaseAndInventory$defective.y / (mergedPurchaseAndInventory$quantity))*100
sum(mergedPurchaseAndInventory$defectiveRate > 1)
## [1] 566
mergedPurchaseAndInventory$returnRate = (mergedPurchaseAndInventory$returns.y / (mergedPurchaseAndInventory$quantity))*100
sum(mergedPurchaseAndInventory$returnRate > 1)
## [1] 15
salesPerSKU = aggregate(accounts2016$sales$qty,by=list(accounts2016$sales$sku),sum)
names(salesPerSKU)[names(salesPerSKU) == "Group.1"] = "sku"
names(salesPerSKU)[names(salesPerSKU) == "x"] = "quantity"
mergedSalesAndInventory = merge(salesPerSKU,accounts2016$allInventoryMatched,by="sku")
mergedSalesAndInventory = subset(mergedSalesAndInventory, select = c(sku,quantity,beginstock,returns.y,defective.y))
mergedSalesAndInventory$defectiveRate = (mergedSalesAndInventory$defective.y / (mergedSalesAndInventory$quantity))*100
sum(mergedSalesAndInventory$defectiveRate > 1)
## [1] 724
mergedSalesAndInventory$returnRate = (mergedSalesAndInventory$returns.y / (mergedSalesAndInventory$quantity))*100
sum(mergedSalesAndInventory$returnRate > 2)
## [1] 0