AR aging reports can be scanned from a file and updated on the datastudio server. However, this is a highly-manual process. we need to work on automation next.

master
dtookey 4 years ago
parent f7b56120ea
commit d36464aa8a

@ -3,6 +3,7 @@ package db
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"io/ioutil"
"log"
"os"
@ -87,6 +88,7 @@ func (c *ConnectorGeneric) CreateTables() {
NewRunner("create-insight-user-table.sql", InsightDatabaseName),
NewRunner("create-insight-timeEntry-table.sql", InsightDatabaseName),
NewRunner("create-mercury-picturePatterns-table.sql", MercuryDatabaseName),
NewRunner("create-mercury-arReports-table.sql", MercuryDatabaseName),
}
for _, runner := range tableCreationRunners {
@ -142,11 +144,11 @@ func QueryForObjects[K any](db *ConnectorGeneric, dbName string, queryScript str
return &rs
}
func BulkUpdate[K any](db *ConnectorGeneric, dbName string, updateScript string, items *[]*K, mapping func(s *sql.Stmt, item *K)) {
db.startConnection(dbName)
defer db.returnConnection()
func BulkUpdate[K any](connector *ConnectorGeneric, dbName string, updateScript string, items *[]*K, mapping func(s *sql.Stmt, item *K)) {
connector.startConnection(dbName)
defer connector.returnConnection()
statement := loadSqlFile(updateScript)
s, err := db.cachedConnection.Prepare(*statement)
s, err := connector.cachedConnection.Prepare(*statement)
if err != nil {
log.Panic(err)
}

@ -0,0 +1,5 @@
package drive
func Test() {
}

@ -0,0 +1,134 @@
package finance
import (
"database/sql"
"fmt"
"log"
"mercury/src/db"
"os"
"path"
"strconv"
"strings"
)
var (
ArInputHeaders = []string{"Acct Name", "Current", "30", "60", "90", ">90"}
arReportInputLineMappingFunction = func(s *sql.Stmt, item *arReportInputLine) {
_, err := s.Exec(
item.AccountName,
item.Current,
item.Thirty,
item.Sixty,
item.Ninety,
item.OverNinety,
item.TotalForPeriod,
item.ReportNameInfo.DateString,
item.ReportNameInfo.Region)
if err != nil {
log.Printf("%#v\n", s)
log.Panic(err)
}
}
)
type (
arReportInputLine struct {
AccountName string
Current float64
Thirty float64
Sixty float64
Ninety float64
OverNinety float64
TotalForPeriod float64
ReportNameInfo *arReportNameInfo
}
arReportNameInfo struct {
DateString string
Region string
}
)
func GenerateArAgingReport(pathlike string) {
connector := &db.ConnectorGeneric{}
res := make([]*arReportInputLine, 0, 5000)
listing, err := os.ReadDir(pathlike)
if err != nil {
log.Panic(err)
}
for _, listing := range listing {
subPath := path.Join(pathlike, listing.Name())
parts := processArReport(subPath)
for _, part := range *parts {
res = append(res, part)
}
}
db.BulkUpdate[arReportInputLine](connector, "mercury", "update-mercury-arReport.sql", &res, arReportInputLineMappingFunction)
}
func processArReport(pathlike string) *[]*arReportInputLine {
res := make([]*arReportInputLine, 0, 500)
contents := readCsv(pathlike)
for _, row := range *contents {
if row[0] == "" || row[0] == "TOTAL" {
continue
}
reportNameInfo := parseReportNameToInfo(pathlike)
inputLine := rawRowToArReportInputLine(row, reportNameInfo)
res = append(res, inputLine)
}
return &res
}
func parseReportNameToInfo(pathlike string) *arReportNameInfo {
base := path.Base(pathlike)
parts := strings.Split(base, "_")
date := reformatSolidDateString(parts[0])
region := parts[2]
return &arReportNameInfo{date, region}
}
func reformatSolidDateString(dateString string) string {
year := dateString[:4]
month := dateString[4:6]
day := dateString[6:8]
return fmt.Sprintf("%s-%s-%s", year, month, day)
}
func rawRowToArReportInputLine(row []string, fileNameInfo *arReportNameInfo) *arReportInputLine {
res := arReportInputLine{}
res.AccountName = row[0]
current, err := strconv.ParseFloat(row[1], 64)
if err != nil {
log.Panicln(err)
}
thirty, err := strconv.ParseFloat(row[2], 64)
if err != nil {
log.Panicln(err)
}
sixty, err := strconv.ParseFloat(row[3], 64)
if err != nil {
log.Panicln(err)
}
ninety, err := strconv.ParseFloat(row[4], 64)
if err != nil {
log.Panicln(err)
}
overninety, err := strconv.ParseFloat(row[5], 64)
if err != nil {
log.Panicln(err)
}
res.Current = current
res.Thirty = thirty
res.Sixty = sixty
res.Ninety = ninety
res.OverNinety = overninety
res.TotalForPeriod = current + thirty + sixty + ninety + overninety
res.ReportNameInfo = fileNameInfo
return &res
}

@ -1,4 +1,4 @@
package mercury
package finance
import (
"encoding/csv"
@ -202,25 +202,6 @@ func ProcessCSVsFromPath(pathlikeIn string, pathlikeOut string) {
}
func createCsvWriter(pathlike string) *csv.Writer {
_, err := os.Stat(pathlike)
var file *os.File
if os.IsNotExist(err) {
file, err = os.Create(pathlike)
if err != nil {
log.Panic(err)
}
} else {
os.Remove(pathlike)
file, err = os.Create(pathlike)
if err != nil {
log.Panic(err)
}
}
writer := csv.NewWriter(file)
return writer
}
func processFile(pathlike *string) *[]*BillingLine {
ret := make([]*BillingLine, 0, 500000)
content, err := os.OpenFile(*pathlike, os.O_RDONLY, 0755)
@ -265,11 +246,6 @@ func processFile(pathlike *string) *[]*BillingLine {
return &ret
}
func convertDateToSqlDate(datelike string) string {
parts := strings.Split(datelike, "/")
return fmt.Sprintf("%s-%s-%s", parts[2], parts[0], parts[1])
}
func enumFiles(pathlikebase string) *[]*string {
files, err := os.ReadDir(pathlikebase)
ret := make([]*string, 0, 100)
@ -290,3 +266,49 @@ func (b *BillingLine) toRow() []string {
func (t *TrialBalanceLine) toRow() []string {
return []string{t.AccountName, strconv.FormatFloat(t.Amount, 'f', 2, 64), t.Period, t.AccountType}
}
//<editor-fold name="util">
/*======================================================================================
util
======================================================================================*/
func convertDateToSqlDate(datelike string) string {
parts := strings.Split(datelike, "/")
return fmt.Sprintf("%s-%s-%s", parts[2], parts[0], parts[1])
}
func readCsv(pathlike string) *[][]string {
file, err := os.OpenFile(pathlike, os.O_RDONLY, 0755)
if err != nil {
log.Panic(err)
}
defer file.Close()
reader := csv.NewReader(file)
contents, err := reader.ReadAll()
if err != nil {
log.Panic(err)
}
return &contents
}
func createCsvWriter(pathlike string) *csv.Writer {
_, err := os.Stat(pathlike)
var file *os.File
if os.IsNotExist(err) {
file, err = os.Create(pathlike)
if err != nil {
log.Panic(err)
}
} else {
os.Remove(pathlike)
file, err = os.Create(pathlike)
if err != nil {
log.Panic(err)
}
}
writer := csv.NewWriter(file)
return writer
}
//</editor-fold>

@ -2,6 +2,7 @@ package main
import (
"log"
"mercury/src/finance"
"mercury/src/mercury"
"mercury/src/projectClarity"
"os"
@ -42,5 +43,5 @@ func processQbBilling() {
log.Fatalln("please set the mercury_qb_path env var. we don't know where to look otherwise")
}
mercury.ProcessTrialBalances(reportBase, "./updateInsightData.csv")
finance.ProcessTrialBalances(reportBase, "./updateInsightData.csv")
}

@ -1,8 +1,10 @@
package projectClarity
import (
"database/sql"
"log"
"mercury/src/projectInsight"
"mercury/src/db"
"regexp"
)
//<editor-fold name="Snitch">
@ -11,11 +13,11 @@ import (
======================================================================================*/
type Snitch struct {
DB projectInsight.InsightDBConnector
DB *db.ConnectorGeneric
}
func NewSnitch() *Snitch {
return &Snitch{}
return &Snitch{&db.ConnectorGeneric{}}
}
func (s *Snitch) Test() {
@ -30,6 +32,10 @@ func (s *Snitch) getProjectNumbersForTesting() *[]*string {
return nil
}
func (s *Snitch) getPatterns() *[]*picturePattern {
return db.QueryForObjects[picturePattern](s.DB, db.MercuryDatabaseName, "read-mercury-picturePatterns.sql", mercuryPicturePatternMappingFunction)
}
//</editor-fold>
//<editor-fold name="utility functions">
@ -38,3 +44,26 @@ func (s *Snitch) getProjectNumbersForTesting() *[]*string {
======================================================================================*/
//</editor-fold>
//<editor-fold name="scratch">
/*======================================================================================
scratch
======================================================================================*/
type picturePattern struct {
id int
rawPattern string
pattern regexp.Regexp
verified bool
}
var mercuryPicturePatternMappingFunction = func(rows *sql.Rows) *picturePattern {
pattern := picturePattern{}
err := rows.Scan(&pattern.id, &pattern.rawPattern, &pattern.verified)
if err != nil {
log.Panic(err)
}
return &pattern
}
//</editor-fold>

@ -2,7 +2,7 @@ package projectInsight
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"log"
"mercury/src/db"
)

@ -0,0 +1,14 @@
DROP TABLE IF EXISTS mercury.ar_aging_report;
CREATE TABLE mercury.ar_aging_report
(
AccountName VARCHAR(150),
Current REAL,
Thirty REAL,
Sixty REAL,
Ninety REAL,
OverNinety REAL,
Total_for_Period REAL,
DateExported Date,
Region VARCHAR(25)
);

@ -0,0 +1 @@
select * from picture_patterns;

@ -0,0 +1,2 @@
INSERT INTO mercury.ar_aging_report (AccountName, Current, Thirty, Sixty, Ninety, OverNinety, Total_for_Period, DateExported, Region)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);

@ -0,0 +1,12 @@
package main
import (
"mercury/src/finance"
"mercury/src/mercury"
)
func main() {
ic := mercury.NewInterconnect()
ic.ResetTables()
finance.GenerateArAgingReport("/home/dtookey/work/clarity-reporting/qb/ar/")
}
Loading…
Cancel
Save