diff --git a/Engine b/Engine new file mode 100755 index 0000000..ebc42b6 Binary files /dev/null and b/Engine differ diff --git a/internal/database/refrence-repo.go b/internal/database/refrence-repo.go new file mode 100644 index 0000000..0e21684 --- /dev/null +++ b/internal/database/refrence-repo.go @@ -0,0 +1,177 @@ +package database + +import ( + "context" + "fmt" +) + +// ── Domain types ────────────────────────────────────────────────────────────── + +type Department struct { + ID int `json:"id"` + Code string `json:"code"` + Name string `json:"name"` + CostCenter string `json:"cost_center"` + Active bool `json:"active"` + CreatedAt string `json:"created_at"` +} + +type GLAccount struct { + ID int `json:"id"` + Code string `json:"code"` + Description string `json:"description"` + Type string `json:"type"` // revenue | cogs | opex | capex | headcount + FavourHigh bool `json:"favour_high"` // true = over-budget is good (revenue accounts) + Active bool `json:"active"` +} + +// ── ReferenceRepo ───────────────────────────────────────────────────────────── + +type ReferenceRepo struct { + db *DB +} + +func NewReferenceRepo(db *DB) *ReferenceRepo { + return &ReferenceRepo{db: db} +} + +// ── Department operations ───────────────────────────────────────────────────── + +func (r *ReferenceRepo) CreateDepartment(ctx context.Context, d Department) (*Department, error) { + res, err := r.db.ExecContext(ctx, ` + INSERT INTO departments (code, name, cost_center, active) + VALUES (?, ?, ?, ?) + ON CONFLICT(code) DO UPDATE + SET name = excluded.name, + cost_center = excluded.cost_center, + active = excluded.active`, + d.Code, d.Name, d.CostCenter, boolToInt(d.Active), + ) + if err != nil { + return nil, fmt.Errorf("upsert department: %w", err) + } + + id, err := res.LastInsertId() + if err != nil || id == 0 { + // ON CONFLICT branch — fetch existing row + return r.getDepartmentByCode(ctx, d.Code) + } + d.ID = int(id) + return &d, nil +} + +func (r *ReferenceRepo) getDepartmentByCode(ctx context.Context, code string) (*Department, error) { + var d Department + var active int + err := r.db.QueryRowContext(ctx, + `SELECT id, code, name, cost_center, active, created_at FROM departments WHERE code = ?`, code, + ).Scan(&d.ID, &d.Code, &d.Name, &d.CostCenter, &active, &d.CreatedAt) + if err != nil { + return nil, fmt.Errorf("fetch department by code: %w", err) + } + d.Active = active == 1 + return &d, nil +} + +func (r *ReferenceRepo) ListDepartments(ctx context.Context) ([]Department, error) { + rows, err := r.db.QueryContext(ctx, + `SELECT id, code, name, cost_center, active, created_at + FROM departments ORDER BY code`) + if err != nil { + return nil, err + } + defer rows.Close() + + var depts []Department + for rows.Next() { + var d Department + var active int + if err := rows.Scan(&d.ID, &d.Code, &d.Name, &d.CostCenter, &active, &d.CreatedAt); err != nil { + return nil, err + } + d.Active = active == 1 + depts = append(depts, d) + } + return depts, rows.Err() +} + +func (r *ReferenceRepo) DeleteDepartment(ctx context.Context, id int) error { + _, err := r.db.ExecContext(ctx, `DELETE FROM departments WHERE id = ?`, id) + return err +} + +// ── GL Account operations ───────────────────────────────────────────────────── + +func (r *ReferenceRepo) CreateGLAccount(ctx context.Context, a GLAccount) (*GLAccount, error) { + res, err := r.db.ExecContext(ctx, ` + INSERT INTO gl_accounts (code, description, type, favour_high, active) + VALUES (?, ?, ?, ?, ?) + ON CONFLICT(code) DO UPDATE + SET description = excluded.description, + type = excluded.type, + favour_high = excluded.favour_high, + active = excluded.active`, + a.Code, a.Description, a.Type, boolToInt(a.FavourHigh), boolToInt(a.Active), + ) + if err != nil { + return nil, fmt.Errorf("upsert gl_account: %w", err) + } + + id, err := res.LastInsertId() + if err != nil || id == 0 { + return r.getGLAccountByCode(ctx, a.Code) + } + a.ID = int(id) + return &a, nil +} + +func (r *ReferenceRepo) getGLAccountByCode(ctx context.Context, code string) (*GLAccount, error) { + var a GLAccount + var favourHigh, active int + err := r.db.QueryRowContext(ctx, + `SELECT id, code, description, type, favour_high, active FROM gl_accounts WHERE code = ?`, code, + ).Scan(&a.ID, &a.Code, &a.Description, &a.Type, &favourHigh, &active) + if err != nil { + return nil, fmt.Errorf("fetch gl_account by code: %w", err) + } + a.FavourHigh = favourHigh == 1 + a.Active = active == 1 + return &a, nil +} + +func (r *ReferenceRepo) ListGLAccounts(ctx context.Context) ([]GLAccount, error) { + rows, err := r.db.QueryContext(ctx, + `SELECT id, code, description, type, favour_high, active + FROM gl_accounts ORDER BY code`) + if err != nil { + return nil, err + } + defer rows.Close() + + var accts []GLAccount + for rows.Next() { + var a GLAccount + var favourHigh, active int + if err := rows.Scan(&a.ID, &a.Code, &a.Description, &a.Type, &favourHigh, &active); err != nil { + return nil, err + } + a.FavourHigh = favourHigh == 1 + a.Active = active == 1 + accts = append(accts, a) + } + return accts, rows.Err() +} + +func (r *ReferenceRepo) DeleteGLAccount(ctx context.Context, id int) error { + _, err := r.db.ExecContext(ctx, `DELETE FROM gl_accounts WHERE id = ?`, id) + return err +} + +// ── helpers ─────────────────────────────────────────────────────────────────── + +func boolToInt(b bool) int { + if b { + return 1 + } + return 0 +} diff --git a/internal/handler/refrence.go b/internal/handler/refrence.go new file mode 100644 index 0000000..33181d8 --- /dev/null +++ b/internal/handler/refrence.go @@ -0,0 +1,144 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + "strings" + + "Engine/internal/database" +) + +type ReferenceHandler struct { + repo *database.ReferenceRepo +} + +func NewReferenceHandler(repo *database.ReferenceRepo) *ReferenceHandler { + return &ReferenceHandler{repo: repo} +} + +// ── Departments ─────────────────────────────────────────────────────────────── + +// POST /api/v1/departments +// PUT /api/v1/departments/{id} (same body, id from path) +func (h *ReferenceHandler) CreateDepartment(w http.ResponseWriter, r *http.Request) { + var req database.Department + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid body: %v", err)) + return + } + req.Code = strings.TrimSpace(req.Code) + req.Name = strings.TrimSpace(req.Name) + if req.Code == "" || req.Name == "" { + writeError(w, http.StatusBadRequest, "code and name are required") + return + } + // Default active to true when not explicitly set to false + if !req.Active { + req.Active = true + } + + dept, err := h.repo.CreateDepartment(r.Context(), req) + if err != nil { + writeError(w, http.StatusInternalServerError, fmt.Sprintf("create department: %v", err)) + return + } + writeJSON(w, http.StatusCreated, dept) +} + +// GET /api/v1/departments +func (h *ReferenceHandler) ListDepartments(w http.ResponseWriter, r *http.Request) { + depts, err := h.repo.ListDepartments(r.Context()) + if err != nil { + writeError(w, http.StatusInternalServerError, fmt.Sprintf("list departments: %v", err)) + return + } + if depts == nil { + depts = []database.Department{} + } + writeJSON(w, http.StatusOK, depts) +} + +// DELETE /api/v1/departments/{id} +func (h *ReferenceHandler) DeleteDepartment(w http.ResponseWriter, r *http.Request) { + id, err := pathID(r, "id") + if err != nil { + writeError(w, http.StatusBadRequest, "invalid id") + return + } + if err := h.repo.DeleteDepartment(r.Context(), id); err != nil { + writeError(w, http.StatusInternalServerError, fmt.Sprintf("delete department: %v", err)) + return + } + w.WriteHeader(http.StatusNoContent) +} + +// ── GL Accounts ─────────────────────────────────────────────────────────────── + +var validGLTypes = map[string]bool{ + "revenue": true, "cogs": true, "opex": true, "capex": true, "headcount": true, +} + +// POST /api/v1/gl-accounts +func (h *ReferenceHandler) CreateGLAccount(w http.ResponseWriter, r *http.Request) { + var req database.GLAccount + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid body: %v", err)) + return + } + req.Code = strings.TrimSpace(req.Code) + req.Description = strings.TrimSpace(req.Description) + if req.Code == "" || req.Description == "" { + writeError(w, http.StatusBadRequest, "code and description are required") + return + } + if !validGLTypes[req.Type] { + writeError(w, http.StatusBadRequest, + "type must be one of: revenue, cogs, opex, capex, headcount") + return + } + if !req.Active { + req.Active = true + } + + acct, err := h.repo.CreateGLAccount(r.Context(), req) + if err != nil { + writeError(w, http.StatusInternalServerError, fmt.Sprintf("create gl_account: %v", err)) + return + } + writeJSON(w, http.StatusCreated, acct) +} + +// GET /api/v1/gl-accounts +func (h *ReferenceHandler) ListGLAccounts(w http.ResponseWriter, r *http.Request) { + accts, err := h.repo.ListGLAccounts(r.Context()) + if err != nil { + writeError(w, http.StatusInternalServerError, fmt.Sprintf("list gl_accounts: %v", err)) + return + } + if accts == nil { + accts = []database.GLAccount{} + } + writeJSON(w, http.StatusOK, accts) +} + +// DELETE /api/v1/gl-accounts/{id} +func (h *ReferenceHandler) DeleteGLAccount(w http.ResponseWriter, r *http.Request) { + id, err := pathID(r, "id") + if err != nil { + writeError(w, http.StatusBadRequest, "invalid id") + return + } + if err := h.repo.DeleteGLAccount(r.Context(), id); err != nil { + writeError(w, http.StatusInternalServerError, fmt.Sprintf("delete gl_account: %v", err)) + return + } + w.WriteHeader(http.StatusNoContent) +} + +// ── Shared helpers ──────────────────────────────────────────────────────────── + +func pathID(r *http.Request, key string) (int, error) { + return strconv.Atoi(r.PathValue(key)) +} diff --git a/main.go b/main.go index 01c5146..89822a3 100644 --- a/main.go +++ b/main.go @@ -34,23 +34,39 @@ func main() { os.Exit(1) } + // Reference data (FK parents — must exist before budgets/actuals) + referenceRepo := database.NewReferenceRepo(db) + referenceH := handler.NewReferenceHandler(referenceRepo) + + // Core FP&A budgetRepo := database.NewBudgetRepo(db) actualsRepo := database.NewActualsRepo(db) budgetSvc := service.NewBudgetService(budgetRepo) varianceSvc := service.NewVarianceService(budgetRepo, actualsRepo) - budgetH := handler.NewBudgetHandler(budgetSvc) actualsH := handler.NewActualsHandler(actualsRepo) varianceH := handler.NewVarianceHandler(varianceSvc) - mux := http.NewServeMux() + // Reference endpoints + mux.HandleFunc("POST /api/v1/departments", referenceH.CreateDepartment) + mux.HandleFunc("GET /api/v1/departments", referenceH.ListDepartments) + mux.HandleFunc("DELETE /api/v1/departments/{id}", referenceH.DeleteDepartment) + + mux.HandleFunc("POST /api/v1/gl-accounts", referenceH.CreateGLAccount) + mux.HandleFunc("GET /api/v1/gl-accounts", referenceH.ListGLAccounts) + mux.HandleFunc("DELETE /api/v1/gl-accounts/{id}", referenceH.DeleteGLAccount) + + // Budget endpoints mux.HandleFunc("POST /api/v1/budgets", budgetH.Create) mux.HandleFunc("PUT /api/v1/budgets/{id}", budgetH.Update) mux.HandleFunc("DELETE /api/v1/budgets/{id}", budgetH.Delete) + + // Actuals + variance mux.HandleFunc("POST /api/v1/actuals/ingest", actualsH.Ingest) mux.HandleFunc("GET /api/v1/variance", varianceH.Report) mux.HandleFunc("GET /api/v1/variance/alerts", varianceH.Alerts) + mux.HandleFunc("GET /api/v1/health", func(w http.ResponseWriter, r *http.Request) { if err := db.PingContext(r.Context()); err != nil { http.Error(w, `{"error":"db unhealthy"}`, http.StatusServiceUnavailable) diff --git a/testing/README.md b/testing/README.md new file mode 100644 index 0000000..0c77d27 --- /dev/null +++ b/testing/README.md @@ -0,0 +1,90 @@ +# FP&A Test Data Platform + +Python tooling to generate, validate, and load realistic FP&A data into your Go API. + +## Structure + +``` +testing/ +├── generators/ +│ └── generate_data.py # Creates all CSV files +├── loaders/ +│ └── api_loader.py # POSTs CSVs to your Go API +├── tests/ +│ └── test_fpa.py # Data integrity + API tests +├── data/ +│ └── csv/ # Generated CSV files land here +└── requirements.txt +``` + +## Quick Start + +```bash +pip install -r requirements.txt + +# 1. Generate all CSV data +python generators/generate_data.py + +# 2. Validate data integrity (no API needed) +pytest tests/test_fpa.py -v + +# 3. Load into your Go API (dry-run first) +python loaders/api_loader.py --dry-run + +# 4. Load for real +python loaders/api_loader.py --url http://localhost:8080 +``` + +## Generated Datasets + +| File | Rows | Description | +|------|------|-------------| +| `revenue_budget_vs_actuals.csv` | 48 | Product & Service revenue — budget vs actuals, 24 months | +| `opex_budget_vs_actuals.csv` | ~2,688 | Dept × category opex — budget vs actuals | +| `pl_income_statement.csv` | 24 | Monthly P&L: revenue, COGS, gross profit, EBITDA, net income | +| `cash_flow.csv` | 24 | Operating / investing / financing cash flows, rolling balance | +| `headcount_workforce.csv` | ~1,000+ | Employee snapshots per month, with hire/term dates & salaries | + +## Key Concepts + +**Budget vs Actuals** — Every financial row has a `budget_amount` (what was planned) and +`actual_amount` (what really happened). The `variance` = actual − budget. Positive variance +on revenue = good. Positive variance on spend = over budget. + +**Product vs Service revenue** — Product is recurring SaaS subscriptions (~70%, higher margin). +Service is consulting/support (~30%, lower margin). Both grow monthly at different rates. + +**Cash Flow** — Separate from revenue. Collections can lag invoicing (DSO effect). Includes +a simulated Series A raise in June 2023. + +## API Endpoints (from main.go) + +``` +POST /api/v1/budgets ← create one budget line +PUT /api/v1/budgets/{id} ← update a budget line +DELETE /api/v1/budgets/{id} ← delete a budget line +POST /api/v1/actuals/ingest ← bulk ingest actuals { "records": [...] } +GET /api/v1/variance ← variance report (budget vs actuals) +GET /api/v1/variance/alerts ← over/under budget alerts +GET /api/v1/health ← db health check +``` + +## Load Order + +The seeder always loads in this order — **budgets must exist before actuals**: + +1. **Budgets** — `POST /api/v1/budgets` individually (revenue + opex lines) +2. **Actuals** — `POST /api/v1/actuals/ingest` in batches of 50 +3. **Variance check** — `GET /api/v1/variance` to confirm data landed + +## Loader Options + +```bash +python loaders/api_loader.py --help + + --url API base URL (default: http://localhost:8080) + --batch Records per actuals/ingest request (default: 50) + --dry-run Print payloads without sending + --token Bearer token for auth header + --only Run one step only: budgets | actuals | variance | alerts +``` \ No newline at end of file diff --git a/testing/data/csv/cash_flow.csv b/testing/data/csv/cash_flow.csv new file mode 100644 index 0000000..9d1d76d --- /dev/null +++ b/testing/data/csv/cash_flow.csv @@ -0,0 +1,25 @@ +company,year,month,period,cash_collected_product,cash_collected_service,cash_paid_opex,cash_paid_cogs,net_operating_cash_flow,capex,net_investing_cash_flow,loan_repayment,equity_raised,net_financing_cash_flow,net_change_in_cash,closing_cash_balance +AcmeSaaS Inc.,2023,1,2023-01,163879.88,63878.3,261115.75,83104.06,-116461.63,6403.87,-6403.87,0.0,0.0,0.0,-122865.5,1077134.5 +AcmeSaaS Inc.,2023,2,2023-02,178115.66,69069.82,255846.74,76652.29,-85313.55,5786.84,-5786.84,0.0,0.0,0.0,-91100.39,986034.11 +AcmeSaaS Inc.,2023,3,2023-03,183078.97,71995.04,275445.39,76630.76,-97002.14,0.0,-0.0,5282.91,0.0,-5282.91,-102285.05,883749.06 +AcmeSaaS Inc.,2023,4,2023-04,185844.77,72290.05,275275.82,85242.75,-102383.75,0.0,-0.0,0.0,0.0,0.0,-102383.75,781365.31 +AcmeSaaS Inc.,2023,5,2023-05,199095.24,71446.54,276860.19,80225.21,-86543.62,7899.21,-7899.21,0.0,0.0,0.0,-94442.83,686922.48 +AcmeSaaS Inc.,2023,6,2023-06,186599.26,73957.63,282518.88,93079.84,-115041.83,0.0,-0.0,4741.08,500000.0,495258.92,380217.09,1067139.57 +AcmeSaaS Inc.,2023,7,2023-07,199774.68,71671.33,273476.83,86473.31,-88504.13,0.0,-0.0,0.0,0.0,0.0,-88504.13,978635.44 +AcmeSaaS Inc.,2023,8,2023-08,206079.53,71897.22,286478.13,88563.47,-97064.85,7988.9,-7988.9,0.0,0.0,0.0,-105053.75,873581.69 +AcmeSaaS Inc.,2023,9,2023-09,208715.04,76023.16,284930.26,98913.54,-99105.6,0.0,-0.0,4705.79,0.0,-4705.79,-103811.39,769770.3 +AcmeSaaS Inc.,2023,10,2023-10,202037.42,77115.77,302273.74,99395.35,-122515.9,10447.72,-10447.72,0.0,0.0,0.0,-132963.62,636806.68 +AcmeSaaS Inc.,2023,11,2023-11,224870.49,81643.64,292828.92,97493.13,-83807.92,0.0,-0.0,0.0,0.0,0.0,-83807.92,552998.76 +AcmeSaaS Inc.,2023,12,2023-12,224481.39,82875.26,304003.52,105371.0,-102017.87,0.0,-0.0,5589.79,0.0,-5589.79,-107607.66,445391.1 +AcmeSaaS Inc.,2024,1,2024-01,225535.21,86372.23,306496.22,94450.84,-89039.62,0.0,-0.0,0.0,0.0,0.0,-89039.62,356351.48 +AcmeSaaS Inc.,2024,2,2024-02,231002.6,81570.67,309999.41,104620.68,-102046.82,0.0,-0.0,0.0,0.0,0.0,-102046.82,254304.66 +AcmeSaaS Inc.,2024,3,2024-03,255843.55,86327.17,312424.94,108228.6,-78482.82,0.0,-0.0,5631.5,0.0,-5631.5,-84114.32,170190.34 +AcmeSaaS Inc.,2024,4,2024-04,243746.7,86737.49,333771.4,110195.35,-113482.56,0.0,-0.0,0.0,0.0,0.0,-113482.56,56707.78 +AcmeSaaS Inc.,2024,5,2024-05,253076.43,92057.75,335130.33,110912.96,-100909.11,0.0,-0.0,0.0,0.0,0.0,-100909.11,-44201.33 +AcmeSaaS Inc.,2024,6,2024-06,264225.36,88606.97,327425.58,118967.82,-93561.07,0.0,-0.0,4037.87,0.0,-4037.87,-97598.94,-141800.27 +AcmeSaaS Inc.,2024,7,2024-07,260739.02,83323.95,340807.79,120273.6,-117018.42,5198.6,-5198.6,0.0,0.0,0.0,-122217.02,-264017.29 +AcmeSaaS Inc.,2024,8,2024-08,261496.73,85375.07,335055.79,111383.21,-99567.2,6990.89,-6990.89,0.0,0.0,0.0,-106558.09,-370575.38 +AcmeSaaS Inc.,2024,9,2024-09,275303.9,94869.09,351269.66,114058.86,-95155.53,0.0,-0.0,5116.17,0.0,-5116.17,-100271.7,-470847.08 +AcmeSaaS Inc.,2024,10,2024-10,277726.03,99258.56,336970.32,118601.63,-78587.36,0.0,-0.0,0.0,0.0,0.0,-78587.36,-549434.44 +AcmeSaaS Inc.,2024,11,2024-11,309709.89,98087.4,341625.12,132656.81,-66484.64,10909.5,-10909.5,0.0,0.0,0.0,-77394.14,-626828.58 +AcmeSaaS Inc.,2024,12,2024-12,312778.51,91878.14,353027.96,122611.82,-70983.13,6093.24,-6093.24,5077.16,0.0,-5077.16,-82153.53,-708982.11 diff --git a/testing/data/csv/headcount_workforce.csv b/testing/data/csv/headcount_workforce.csv new file mode 100644 index 0000000..4aab9af --- /dev/null +++ b/testing/data/csv/headcount_workforce.csv @@ -0,0 +1,422 @@ +company,employee_id,department,role,hire_date,termination_date,status,annual_salary_budget,actual_salary_paid_ytd,year,month,period,headcount_fte +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,14850.0,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,13466.67,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,13466.67,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,9900.0,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,5308.33,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,5362.5,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,5416.67,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,6800.0,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,9075.0,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,6666.67,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,9258.33,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,7916.67,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,9775.0,2023,1,2023-01,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,7425.0,2023,1,2023-01,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,30300.0,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,26400.0,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,26933.33,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,20200.0,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,10941.67,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,10616.67,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,10725.0,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,13200.0,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,18516.67,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,13200.0,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,18333.33,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,15991.67,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,19166.67,2023,2,2023-02,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,15000.0,2023,2,2023-02,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,44100.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,40800.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,40000.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,30600.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,15925.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,16250.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,16250.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,19800.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,27225.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,20000.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,27500.0,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,23512.5,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,28462.5,2023,3,2023-03,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,22275.0,2023,3,2023-03,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,60000.0,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,54400.0,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,53866.67,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,39600.0,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,21233.33,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,22100.0,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,21450.0,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,26666.67,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,36300.0,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,26400.0,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,36300.0,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,31983.33,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,38333.33,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,30000.0,2023,4,2023-04,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,6250.0,2023,4,2023-04,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,75750.0,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,66000.0,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,66000.0,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,50500.0,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,27354.17,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,26541.67,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,27083.33,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,33666.67,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,44916.67,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,33333.33,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,44916.67,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,39583.33,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,47916.67,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,37500.0,2023,5,2023-05,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,12375.0,2023,5,2023-05,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,88200.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,79200.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,80000.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,58800.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,32500.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,32825.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,32500.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,40000.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,55000.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,39200.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,55000.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,47975.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,58075.0,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,45000.0,2023,6,2023-06,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,18937.5,2023,6,2023-06,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,102900.0,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,95200.0,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,95200.0,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,68600.0,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,38675.0,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,37158.33,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,37916.67,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,47133.33,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,64808.33,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,47600.0,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,64808.33,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,55416.67,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,68425.0,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,52500.0,2023,7,2023-07,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,25250.0,2023,7,2023-07,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,10200.0,2023,7,2023-07,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,121200.0,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,106666.67,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,107733.33,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,79200.0,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,42900.0,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,43333.33,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,43333.33,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,53866.67,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,72600.0,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,52266.67,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,71866.67,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,62700.0,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,77433.33,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,60000.0,2023,8,2023-08,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,31250.0,2023,8,2023-08,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,20200.0,2023,8,2023-08,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,133650.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,122400.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,121200.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,88200.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,49237.5,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,48750.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,49237.5,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,61200.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,80850.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,59400.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,80850.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,69825.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,86250.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,68175.0,2023,9,2023-09,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,36750.0,2023,9,2023-09,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,30300.0,2023,9,2023-09,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,148500.0,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,134666.67,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,130666.67,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,100000.0,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,53625.0,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,,Active,65000,54708.33,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,54708.33,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,66000.0,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,91666.67,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,67333.33,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,90750.0,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,78375.0,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,94875.0,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,75750.0,2023,10,2023-10,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,43312.5,2023,10,2023-10,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,39600.0,2023,10,2023-10,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,166650.0,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,146666.67,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,146666.67,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,112200.0,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,60775.0,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,60179.17,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,60179.17,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,72600.0,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,100833.33,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,72600.0,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,100833.33,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,87954.17,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,105416.67,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,83325.0,2023,11,2023-11,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,49500.0,2023,11,2023-11,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,50500.0,2023,11,2023-11,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,178200.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,158400.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,158400.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,121200.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,66300.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,65650.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,63700.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,80800.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,108900.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,79200.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,111100.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,,Active,95000,95000.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,116150.0,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,89100.0,2023,12,2023-12,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,56812.5,2023,12,2023-12,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,60600.0,2023,12,2023-12,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,10833.33,2023,12,2023-12,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,14850.0,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,13600.0,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,13200.0,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,9900.0,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,5470.83,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,5470.83,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,5470.83,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,6800.0,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,,Active,110000,9258.33,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,6733.33,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,9166.67,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,7837.5,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,9487.5,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,7425.0,2024,1,2024-01,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,6250.0,2024,1,2024-01,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,10100.0,2024,1,2024-01,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,10725.0,2024,1,2024-01,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,29400.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,27200.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,26666.67,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,20200.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,11050.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,10725.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,10833.33,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,13333.33,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,18150.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,13200.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,18150.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,16150.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,19166.67,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,15000.0,2024,2,2024-02,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,12375.0,2024,2,2024-02,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,19800.0,2024,2,2024-02,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,21666.67,2024,2,2024-02,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,45450.0,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,40800.0,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,40400.0,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,30000.0,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,16087.5,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,16412.5,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,16087.5,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,20200.0,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,27500.0,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,20400.0,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,27500.0,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,23987.5,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,28175.0,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,22050.0,2024,3,2024-03,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,18937.5,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,30600.0,2024,3,2024-03,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,32825.0,2024,3,2024-03,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,7995.83,2024,3,2024-03,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,59400.0,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,53866.67,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,52266.67,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,40000.0,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,21450.0,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,21666.67,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,21883.33,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,26666.67,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,36666.67,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,26666.67,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,35933.33,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,31666.67,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,37566.67,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,30000.0,2024,4,2024-04,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,24750.0,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,40800.0,2024,4,2024-04,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,42466.67,2024,4,2024-04,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,15991.67,2024,4,2024-04,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,74250.0,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,66666.67,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,66000.0,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,49500.0,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,27354.17,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,26541.67,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,27625.0,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,33333.33,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,45375.0,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,33666.67,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,45375.0,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,39979.17,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,46958.33,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,38250.0,2024,5,2024-05,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,31562.5,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,49000.0,2024,5,2024-05,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,54166.67,2024,5,2024-05,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,23750.0,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1018,Sales,Account Executive,2024-05-01,,Active,90000,7500.0,2024,5,2024-05,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,90900.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,79200.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,81600.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,61200.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,33150.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,33150.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,32175.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,40800.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,55550.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,40000.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,54450.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,48450.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,58075.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,44550.0,2024,6,2024-06,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,37500.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,60600.0,2024,6,2024-06,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,64350.0,2024,6,2024-06,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,31983.33,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1018,Sales,Account Executive,2024-05-01,,Active,90000,14700.0,2024,6,2024-06,1.0 +AcmeSaaS Inc.,EMP1019,Sales,Account Executive,2024-06-01,,Active,90000,7425.0,2024,6,2024-06,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,106050.0,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,95200.0,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,92400.0,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,70000.0,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,37916.67,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,38295.83,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,38295.83,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,46666.67,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,64166.67,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,47133.33,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,64808.33,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,55970.83,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,65741.67,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,53025.0,2024,7,2024-07,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,43312.5,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,68600.0,2024,7,2024-07,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,75075.0,2024,7,2024-07,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,38791.67,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1018,Sales,Account Executive,2024-05-01,,Active,90000,22500.0,2024,7,2024-07,1.0 +AcmeSaaS Inc.,EMP1019,Sales,Account Executive,2024-06-01,,Active,90000,15000.0,2024,7,2024-07,0.5 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,120000.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,105600.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,106666.67,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,80000.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,42900.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,43333.33,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,43333.33,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,53866.67,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,72600.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,52800.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,71866.67,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,62700.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,76666.67,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,60600.0,2024,8,2024-08,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,49500.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,80000.0,2024,8,2024-08,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,85800.0,2024,8,2024-08,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,46550.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1018,Sales,Account Executive,2024-05-01,,Active,90000,30600.0,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1019,Sales,Account Executive,2024-06-01,,Active,90000,22500.0,2024,8,2024-08,0.5 +AcmeSaaS Inc.,EMP1020,Marketing,Marketing Manager,2024-08-01,,Active,110000,9258.33,2024,8,2024-08,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,135000.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,120000.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,120000.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,88200.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,47775.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,48750.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,48750.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,60600.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,83325.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,58800.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,82500.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,71962.5,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,87112.5,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,67500.0,2024,9,2024-09,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,57375.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,89100.0,2024,9,2024-09,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,97500.0,2024,9,2024-09,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,55970.83,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1018,Sales,Account Executive,2024-05-01,,Active,90000,37125.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1019,Sales,Account Executive,2024-06-01,,Active,90000,29700.0,2024,9,2024-09,0.5 +AcmeSaaS Inc.,EMP1020,Marketing,Marketing Manager,2024-08-01,,Active,110000,18150.0,2024,9,2024-09,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,148500.0,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,132000.0,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,134666.67,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,99000.0,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,55250.0,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,54708.33,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,54166.67,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,66666.67,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,91666.67,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,66000.0,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,89833.33,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,79166.67,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,96791.67,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,75000.0,2024,10,2024-10,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,63125.0,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,100000.0,2024,10,2024-10,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,108333.33,2024,10,2024-10,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,63966.67,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1018,Sales,Account Executive,2024-05-01,,Active,90000,45450.0,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1019,Sales,Account Executive,2024-06-01,,Active,90000,37500.0,2024,10,2024-10,0.5 +AcmeSaaS Inc.,EMP1020,Marketing,Marketing Manager,2024-08-01,,Active,110000,27225.0,2024,10,2024-10,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,165000.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,145200.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,148133.33,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,111100.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,60775.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,60179.17,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,58987.5,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,72600.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,101841.67,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,72600.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,102850.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,86212.5,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,107525.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,81675.0,2024,11,2024-11,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,69437.5,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,107800.0,2024,11,2024-11,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,120358.33,2024,11,2024-11,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,72675.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1018,Sales,Account Executive,2024-05-01,,Active,90000,51975.0,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1019,Sales,Account Executive,2024-06-01,,Active,90000,45450.0,2024,11,2024-11,0.5 +AcmeSaaS Inc.,EMP1020,Marketing,Marketing Manager,2024-08-01,,Active,110000,37033.33,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1021,Sales,Sales Manager,2024-11-01,,Active,140000,11433.33,2024,11,2024-11,1.0 +AcmeSaaS Inc.,EMP1000,Engineering,Engineering Manager,2022-03-04,,Active,180000,178200.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1001,Engineering,Senior Engineer,2022-04-02,,Active,160000,158400.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1002,Engineering,Senior Engineer,2022-11-08,,Active,160000,158400.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1003,Engineering,Software Engineer,2022-02-14,,Active,120000,120000.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1004,Sales,SDR,2022-08-04,,Active,65000,65000.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1005,Sales,SDR,2022-03-01,2023-11-01,Terminated,65000,65000.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1006,Sales,SDR,2022-03-14,,Active,65000,65000.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1007,Marketing,Content Strategist,2022-08-21,,Active,80000,80000.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1008,Marketing,Marketing Manager,2022-05-11,2024-02-01,Terminated,110000,110000.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1009,Marketing,Content Strategist,2022-11-02,,Active,80000,81600.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1010,Marketing,Marketing Manager,2022-11-19,,Active,110000,111100.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1011,Operations,Finance Analyst,2022-12-24,2024-01-01,Terminated,95000,95000.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1012,Operations,Operations Manager,2022-03-14,,Active,115000,112700.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1013,Sales,Account Executive,2023-01-01,,Active,90000,91800.0,2024,12,2024-12,0.5 +AcmeSaaS Inc.,EMP1014,Operations,Customer Success,2023-04-01,,Active,75000,75750.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1015,Engineering,Software Engineer,2023-07-01,,Active,120000,121200.0,2024,12,2024-12,0.5 +AcmeSaaS Inc.,EMP1016,Engineering,DevOps Engineer,2023-12-01,,Active,130000,130000.0,2024,12,2024-12,0.5 +AcmeSaaS Inc.,EMP1017,Operations,Finance Analyst,2024-03-01,,Active,95000,80750.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1018,Sales,Account Executive,2024-05-01,,Active,90000,58800.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1019,Sales,Account Executive,2024-06-01,,Active,90000,51975.0,2024,12,2024-12,0.5 +AcmeSaaS Inc.,EMP1020,Marketing,Marketing Manager,2024-08-01,,Active,110000,46291.67,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1021,Sales,Sales Manager,2024-11-01,,Active,140000,23100.0,2024,12,2024-12,1.0 +AcmeSaaS Inc.,EMP1022,Operations,Finance Analyst,2024-12-01,,Active,95000,7916.67,2024,12,2024-12,0.5 diff --git a/testing/data/csv/opex_budget_vs_actuals.csv b/testing/data/csv/opex_budget_vs_actuals.csv new file mode 100644 index 0000000..2348afe --- /dev/null +++ b/testing/data/csv/opex_budget_vs_actuals.csv @@ -0,0 +1,673 @@ +company,department,year,month,period,category,budget_amount,actual_amount,variance,variance_pct +AcmeSaaS Inc.,Engineering,2023,1,2023-01,Salaries,16575.56,17009.7,434.14,2.62 +AcmeSaaS Inc.,Engineering,2023,1,2023-01,Software & Tools,10980.09,10113.47,-866.62,-7.89 +AcmeSaaS Inc.,Engineering,2023,1,2023-01,Travel,11090.59,11700.47,609.88,5.5 +AcmeSaaS Inc.,Engineering,2023,1,2023-01,Marketing Spend,7771.27,7143.48,-627.79,-8.08 +AcmeSaaS Inc.,Engineering,2023,1,2023-01,Cloud Infrastructure,8958.55,8699.37,-259.18,-2.89 +AcmeSaaS Inc.,Engineering,2023,1,2023-01,Contractors,22793.22,25471.1,2677.88,11.75 +AcmeSaaS Inc.,Engineering,2023,1,2023-01,Office & Facilities,16830.71,17396.22,565.51,3.36 +AcmeSaaS Inc.,Sales,2023,1,2023-01,Salaries,11002.74,10389.42,-613.32,-5.57 +AcmeSaaS Inc.,Sales,2023,1,2023-01,Software & Tools,12943.91,12046.07,-897.84,-6.94 +AcmeSaaS Inc.,Sales,2023,1,2023-01,Travel,15349.95,16981.62,1631.67,10.63 +AcmeSaaS Inc.,Sales,2023,1,2023-01,Marketing Spend,14333.45,15628.17,1294.72,9.03 +AcmeSaaS Inc.,Sales,2023,1,2023-01,Cloud Infrastructure,6016.93,5749.31,-267.62,-4.45 +AcmeSaaS Inc.,Sales,2023,1,2023-01,Contractors,3022.29,3135.04,112.75,3.73 +AcmeSaaS Inc.,Sales,2023,1,2023-01,Office & Facilities,7330.73,7147.11,-183.62,-2.5 +AcmeSaaS Inc.,Marketing,2023,1,2023-01,Salaries,13332.55,14605.51,1272.96,9.55 +AcmeSaaS Inc.,Marketing,2023,1,2023-01,Software & Tools,7713.33,7527.1,-186.23,-2.41 +AcmeSaaS Inc.,Marketing,2023,1,2023-01,Travel,5321.44,4962.97,-358.47,-6.74 +AcmeSaaS Inc.,Marketing,2023,1,2023-01,Marketing Spend,5096.37,5704.92,608.55,11.94 +AcmeSaaS Inc.,Marketing,2023,1,2023-01,Cloud Infrastructure,8977.47,8998.0,20.53,0.23 +AcmeSaaS Inc.,Marketing,2023,1,2023-01,Contractors,5295.07,4775.19,-519.88,-9.82 +AcmeSaaS Inc.,Marketing,2023,1,2023-01,Office & Facilities,9263.77,8256.87,-1006.9,-10.87 +AcmeSaaS Inc.,Operations,2023,1,2023-01,Salaries,2424.21,2441.15,16.94,0.7 +AcmeSaaS Inc.,Operations,2023,1,2023-01,Software & Tools,6967.02,7754.7,787.68,11.31 +AcmeSaaS Inc.,Operations,2023,1,2023-01,Travel,8411.4,9139.72,728.32,8.66 +AcmeSaaS Inc.,Operations,2023,1,2023-01,Marketing Spend,5165.97,4560.29,-605.68,-11.72 +AcmeSaaS Inc.,Operations,2023,1,2023-01,Cloud Infrastructure,2019.57,2126.55,106.98,5.3 +AcmeSaaS Inc.,Operations,2023,1,2023-01,Contractors,4810.3,5020.08,209.78,4.36 +AcmeSaaS Inc.,Operations,2023,1,2023-01,Office & Facilities,10201.53,10292.05,90.52,0.89 +AcmeSaaS Inc.,Engineering,2023,2,2023-02,Salaries,8498.07,8015.49,-482.58,-5.68 +AcmeSaaS Inc.,Engineering,2023,2,2023-02,Software & Tools,15832.55,15834.78,2.23,0.01 +AcmeSaaS Inc.,Engineering,2023,2,2023-02,Travel,5454.13,5033.49,-420.64,-7.71 +AcmeSaaS Inc.,Engineering,2023,2,2023-02,Marketing Spend,11790.32,12957.92,1167.6,9.9 +AcmeSaaS Inc.,Engineering,2023,2,2023-02,Cloud Infrastructure,12161.98,13243.48,1081.5,8.89 +AcmeSaaS Inc.,Engineering,2023,2,2023-02,Contractors,21965.66,20903.11,-1062.55,-4.84 +AcmeSaaS Inc.,Engineering,2023,2,2023-02,Office & Facilities,20437.29,21118.83,681.54,3.33 +AcmeSaaS Inc.,Sales,2023,2,2023-02,Salaries,12174.64,11660.84,-513.8,-4.22 +AcmeSaaS Inc.,Sales,2023,2,2023-02,Software & Tools,5015.07,4436.7,-578.37,-11.53 +AcmeSaaS Inc.,Sales,2023,2,2023-02,Travel,14584.67,16086.65,1501.98,10.3 +AcmeSaaS Inc.,Sales,2023,2,2023-02,Marketing Spend,11082.32,12089.63,1007.31,9.09 +AcmeSaaS Inc.,Sales,2023,2,2023-02,Cloud Infrastructure,14837.62,16018.69,1181.07,7.96 +AcmeSaaS Inc.,Sales,2023,2,2023-02,Contractors,10940.65,10435.23,-505.42,-4.62 +AcmeSaaS Inc.,Sales,2023,2,2023-02,Office & Facilities,2625.03,2346.52,-278.51,-10.61 +AcmeSaaS Inc.,Marketing,2023,2,2023-02,Salaries,11304.51,10296.31,-1008.2,-8.92 +AcmeSaaS Inc.,Marketing,2023,2,2023-02,Software & Tools,12050.51,11979.02,-71.49,-0.59 +AcmeSaaS Inc.,Marketing,2023,2,2023-02,Travel,2730.37,2763.01,32.64,1.2 +AcmeSaaS Inc.,Marketing,2023,2,2023-02,Marketing Spend,7062.45,6664.22,-398.23,-5.64 +AcmeSaaS Inc.,Marketing,2023,2,2023-02,Cloud Infrastructure,2552.46,2780.61,228.15,8.94 +AcmeSaaS Inc.,Marketing,2023,2,2023-02,Contractors,10034.04,9848.94,-185.1,-1.84 +AcmeSaaS Inc.,Marketing,2023,2,2023-02,Office & Facilities,10090.66,9392.7,-697.96,-6.92 +AcmeSaaS Inc.,Operations,2023,2,2023-02,Salaries,5656.81,5680.67,23.86,0.42 +AcmeSaaS Inc.,Operations,2023,2,2023-02,Software & Tools,7184.34,6530.86,-653.48,-9.1 +AcmeSaaS Inc.,Operations,2023,2,2023-02,Travel,2947.29,2752.55,-194.74,-6.61 +AcmeSaaS Inc.,Operations,2023,2,2023-02,Marketing Spend,3833.23,3684.27,-148.96,-3.89 +AcmeSaaS Inc.,Operations,2023,2,2023-02,Cloud Infrastructure,9309.51,9506.82,197.31,2.12 +AcmeSaaS Inc.,Operations,2023,2,2023-02,Contractors,6542.89,6119.09,-423.8,-6.48 +AcmeSaaS Inc.,Operations,2023,2,2023-02,Office & Facilities,4845.93,4520.54,-325.39,-6.71 +AcmeSaaS Inc.,Engineering,2023,3,2023-03,Salaries,5542.88,5767.67,224.79,4.06 +AcmeSaaS Inc.,Engineering,2023,3,2023-03,Software & Tools,18606.18,17330.11,-1276.07,-6.86 +AcmeSaaS Inc.,Engineering,2023,3,2023-03,Travel,9226.68,8412.47,-814.21,-8.82 +AcmeSaaS Inc.,Engineering,2023,3,2023-03,Marketing Spend,25004.01,27617.51,2613.5,10.45 +AcmeSaaS Inc.,Engineering,2023,3,2023-03,Cloud Infrastructure,23936.18,24344.3,408.12,1.71 +AcmeSaaS Inc.,Engineering,2023,3,2023-03,Contractors,5539.71,5503.38,-36.33,-0.66 +AcmeSaaS Inc.,Engineering,2023,3,2023-03,Office & Facilities,9438.05,10082.75,644.7,6.83 +AcmeSaaS Inc.,Sales,2023,3,2023-03,Salaries,16387.89,17069.75,681.86,4.16 +AcmeSaaS Inc.,Sales,2023,3,2023-03,Software & Tools,6006.93,6704.93,698.0,11.62 +AcmeSaaS Inc.,Sales,2023,3,2023-03,Travel,4434.38,4007.0,-427.38,-9.64 +AcmeSaaS Inc.,Sales,2023,3,2023-03,Marketing Spend,10055.12,9820.12,-235.0,-2.34 +AcmeSaaS Inc.,Sales,2023,3,2023-03,Cloud Infrastructure,9929.42,9546.47,-382.95,-3.86 +AcmeSaaS Inc.,Sales,2023,3,2023-03,Contractors,10660.29,11585.62,925.33,8.68 +AcmeSaaS Inc.,Sales,2023,3,2023-03,Office & Facilities,15068.65,14159.67,-908.98,-6.03 +AcmeSaaS Inc.,Marketing,2023,3,2023-03,Salaries,4905.59,5331.02,425.43,8.67 +AcmeSaaS Inc.,Marketing,2023,3,2023-03,Software & Tools,8457.6,8559.75,102.15,1.21 +AcmeSaaS Inc.,Marketing,2023,3,2023-03,Travel,8090.15,7217.56,-872.59,-10.79 +AcmeSaaS Inc.,Marketing,2023,3,2023-03,Marketing Spend,6119.85,6853.18,733.33,11.98 +AcmeSaaS Inc.,Marketing,2023,3,2023-03,Cloud Infrastructure,5724.81,6186.5,461.69,8.06 +AcmeSaaS Inc.,Marketing,2023,3,2023-03,Contractors,14982.14,16668.52,1686.38,11.26 +AcmeSaaS Inc.,Marketing,2023,3,2023-03,Office & Facilities,8382.24,9239.98,857.74,10.23 +AcmeSaaS Inc.,Operations,2023,3,2023-03,Salaries,11094.12,12386.3,1292.18,11.65 +AcmeSaaS Inc.,Operations,2023,3,2023-03,Software & Tools,3638.2,3433.18,-205.02,-5.64 +AcmeSaaS Inc.,Operations,2023,3,2023-03,Travel,7127.29,7613.21,485.92,6.82 +AcmeSaaS Inc.,Operations,2023,3,2023-03,Marketing Spend,4156.51,4111.63,-44.88,-1.08 +AcmeSaaS Inc.,Operations,2023,3,2023-03,Cloud Infrastructure,6202.92,6088.3,-114.62,-1.85 +AcmeSaaS Inc.,Operations,2023,3,2023-03,Contractors,2461.71,2731.9,270.19,10.98 +AcmeSaaS Inc.,Operations,2023,3,2023-03,Office & Facilities,5961.81,6670.68,708.87,11.89 +AcmeSaaS Inc.,Engineering,2023,4,2023-04,Salaries,14276.52,15126.17,849.65,5.95 +AcmeSaaS Inc.,Engineering,2023,4,2023-04,Software & Tools,17490.56,15631.66,-1858.9,-10.63 +AcmeSaaS Inc.,Engineering,2023,4,2023-04,Travel,6352.66,6481.0,128.34,2.02 +AcmeSaaS Inc.,Engineering,2023,4,2023-04,Marketing Spend,9157.05,9163.31,6.26,0.07 +AcmeSaaS Inc.,Engineering,2023,4,2023-04,Cloud Infrastructure,22436.93,24336.28,1899.35,8.47 +AcmeSaaS Inc.,Engineering,2023,4,2023-04,Contractors,14739.18,13527.38,-1211.8,-8.22 +AcmeSaaS Inc.,Engineering,2023,4,2023-04,Office & Facilities,14008.3,15557.43,1549.13,11.06 +AcmeSaaS Inc.,Sales,2023,4,2023-04,Salaries,4615.79,4334.65,-281.14,-6.09 +AcmeSaaS Inc.,Sales,2023,4,2023-04,Software & Tools,6593.07,6742.63,149.56,2.27 +AcmeSaaS Inc.,Sales,2023,4,2023-04,Travel,14247.02,14655.22,408.2,2.87 +AcmeSaaS Inc.,Sales,2023,4,2023-04,Marketing Spend,15746.68,15441.41,-305.27,-1.94 +AcmeSaaS Inc.,Sales,2023,4,2023-04,Cloud Infrastructure,7516.67,7667.61,150.94,2.01 +AcmeSaaS Inc.,Sales,2023,4,2023-04,Contractors,5359.75,5389.06,29.31,0.55 +AcmeSaaS Inc.,Sales,2023,4,2023-04,Office & Facilities,19769.48,21832.02,2062.54,10.43 +AcmeSaaS Inc.,Marketing,2023,4,2023-04,Salaries,5320.63,5642.25,321.62,6.04 +AcmeSaaS Inc.,Marketing,2023,4,2023-04,Software & Tools,12663.88,11364.7,-1299.18,-10.26 +AcmeSaaS Inc.,Marketing,2023,4,2023-04,Travel,5814.45,5756.24,-58.21,-1.0 +AcmeSaaS Inc.,Marketing,2023,4,2023-04,Marketing Spend,8067.92,9033.08,965.16,11.96 +AcmeSaaS Inc.,Marketing,2023,4,2023-04,Cloud Infrastructure,12025.54,13457.34,1431.8,11.91 +AcmeSaaS Inc.,Marketing,2023,4,2023-04,Contractors,6693.91,6008.34,-685.57,-10.24 +AcmeSaaS Inc.,Marketing,2023,4,2023-04,Office & Facilities,6926.0,6449.19,-476.81,-6.88 +AcmeSaaS Inc.,Operations,2023,4,2023-04,Salaries,3224.88,3382.41,157.53,4.88 +AcmeSaaS Inc.,Operations,2023,4,2023-04,Software & Tools,8213.48,8433.62,220.14,2.68 +AcmeSaaS Inc.,Operations,2023,4,2023-04,Travel,7822.23,8736.93,914.7,11.69 +AcmeSaaS Inc.,Operations,2023,4,2023-04,Marketing Spend,7810.33,8098.96,288.63,3.7 +AcmeSaaS Inc.,Operations,2023,4,2023-04,Cloud Infrastructure,4003.92,3530.97,-472.95,-11.81 +AcmeSaaS Inc.,Operations,2023,4,2023-04,Contractors,2422.49,2606.85,184.36,7.61 +AcmeSaaS Inc.,Operations,2023,4,2023-04,Office & Facilities,7470.37,7110.68,-359.69,-4.81 +AcmeSaaS Inc.,Engineering,2023,5,2023-05,Salaries,20932.03,21458.66,526.63,2.52 +AcmeSaaS Inc.,Engineering,2023,5,2023-05,Software & Tools,27880.52,29336.63,1456.11,5.22 +AcmeSaaS Inc.,Engineering,2023,5,2023-05,Travel,7589.44,7049.55,-539.89,-7.11 +AcmeSaaS Inc.,Engineering,2023,5,2023-05,Marketing Spend,7113.78,7342.97,229.19,3.22 +AcmeSaaS Inc.,Engineering,2023,5,2023-05,Cloud Infrastructure,6902.13,6511.17,-390.96,-5.66 +AcmeSaaS Inc.,Engineering,2023,5,2023-05,Contractors,18153.93,18103.96,-49.97,-0.28 +AcmeSaaS Inc.,Engineering,2023,5,2023-05,Office & Facilities,11070.91,12147.9,1076.99,9.73 +AcmeSaaS Inc.,Sales,2023,5,2023-05,Salaries,18054.52,17023.05,-1031.47,-5.71 +AcmeSaaS Inc.,Sales,2023,5,2023-05,Software & Tools,4616.54,4883.82,267.28,5.79 +AcmeSaaS Inc.,Sales,2023,5,2023-05,Travel,10522.17,10652.68,130.51,1.24 +AcmeSaaS Inc.,Sales,2023,5,2023-05,Marketing Spend,7903.48,7766.31,-137.17,-1.74 +AcmeSaaS Inc.,Sales,2023,5,2023-05,Cloud Infrastructure,3034.35,2677.27,-357.08,-11.77 +AcmeSaaS Inc.,Sales,2023,5,2023-05,Contractors,16717.78,15013.54,-1704.24,-10.19 +AcmeSaaS Inc.,Sales,2023,5,2023-05,Office & Facilities,14328.88,15646.36,1317.48,9.19 +AcmeSaaS Inc.,Marketing,2023,5,2023-05,Salaries,13535.8,14831.93,1296.13,9.58 +AcmeSaaS Inc.,Marketing,2023,5,2023-05,Software & Tools,9005.24,9645.24,640.0,7.11 +AcmeSaaS Inc.,Marketing,2023,5,2023-05,Travel,12659.2,13755.09,1095.89,8.66 +AcmeSaaS Inc.,Marketing,2023,5,2023-05,Marketing Spend,9472.02,10378.89,906.87,9.57 +AcmeSaaS Inc.,Marketing,2023,5,2023-05,Cloud Infrastructure,3979.6,3702.69,-276.91,-6.96 +AcmeSaaS Inc.,Marketing,2023,5,2023-05,Contractors,3718.53,3495.0,-223.53,-6.01 +AcmeSaaS Inc.,Marketing,2023,5,2023-05,Office & Facilities,6004.6,5432.18,-572.42,-9.53 +AcmeSaaS Inc.,Operations,2023,5,2023-05,Salaries,6732.88,7502.38,769.5,11.43 +AcmeSaaS Inc.,Operations,2023,5,2023-05,Software & Tools,7472.59,8029.93,557.34,7.46 +AcmeSaaS Inc.,Operations,2023,5,2023-05,Travel,4075.1,4448.13,373.03,9.15 +AcmeSaaS Inc.,Operations,2023,5,2023-05,Marketing Spend,5598.95,4960.38,-638.57,-11.41 +AcmeSaaS Inc.,Operations,2023,5,2023-05,Cloud Infrastructure,2284.3,2413.99,129.69,5.68 +AcmeSaaS Inc.,Operations,2023,5,2023-05,Contractors,7797.91,7483.85,-314.06,-4.03 +AcmeSaaS Inc.,Operations,2023,5,2023-05,Office & Facilities,7333.71,8091.98,758.27,10.34 +AcmeSaaS Inc.,Engineering,2023,6,2023-06,Salaries,17206.74,18687.59,1480.85,8.61 +AcmeSaaS Inc.,Engineering,2023,6,2023-06,Software & Tools,18304.76,17085.37,-1219.39,-6.66 +AcmeSaaS Inc.,Engineering,2023,6,2023-06,Travel,17357.94,18676.81,1318.87,7.6 +AcmeSaaS Inc.,Engineering,2023,6,2023-06,Marketing Spend,7698.04,7624.7,-73.34,-0.95 +AcmeSaaS Inc.,Engineering,2023,6,2023-06,Cloud Infrastructure,16942.83,16150.68,-792.15,-4.68 +AcmeSaaS Inc.,Engineering,2023,6,2023-06,Contractors,4879.51,5225.38,345.87,7.09 +AcmeSaaS Inc.,Engineering,2023,6,2023-06,Office & Facilities,18448.65,17242.53,-1206.12,-6.54 +AcmeSaaS Inc.,Sales,2023,6,2023-06,Salaries,3263.35,3184.78,-78.57,-2.41 +AcmeSaaS Inc.,Sales,2023,6,2023-06,Software & Tools,6168.95,6881.32,712.37,11.55 +AcmeSaaS Inc.,Sales,2023,6,2023-06,Travel,8485.88,8559.64,73.76,0.87 +AcmeSaaS Inc.,Sales,2023,6,2023-06,Marketing Spend,17677.52,19541.03,1863.51,10.54 +AcmeSaaS Inc.,Sales,2023,6,2023-06,Cloud Infrastructure,19435.57,17641.32,-1794.25,-9.23 +AcmeSaaS Inc.,Sales,2023,6,2023-06,Contractors,7643.4,8506.31,862.91,11.29 +AcmeSaaS Inc.,Sales,2023,6,2023-06,Office & Facilities,13856.24,12787.32,-1068.92,-7.71 +AcmeSaaS Inc.,Marketing,2023,6,2023-06,Salaries,14588.95,14628.95,40.0,0.27 +AcmeSaaS Inc.,Marketing,2023,6,2023-06,Software & Tools,5583.03,5429.2,-153.83,-2.76 +AcmeSaaS Inc.,Marketing,2023,6,2023-06,Travel,3553.81,3619.13,65.32,1.84 +AcmeSaaS Inc.,Marketing,2023,6,2023-06,Marketing Spend,7767.72,7310.46,-457.26,-5.89 +AcmeSaaS Inc.,Marketing,2023,6,2023-06,Cloud Infrastructure,11565.87,12145.42,579.55,5.01 +AcmeSaaS Inc.,Marketing,2023,6,2023-06,Contractors,6205.9,5463.71,-742.19,-11.96 +AcmeSaaS Inc.,Marketing,2023,6,2023-06,Office & Facilities,9985.33,11005.21,1019.88,10.21 +AcmeSaaS Inc.,Operations,2023,6,2023-06,Salaries,5946.87,5704.52,-242.35,-4.08 +AcmeSaaS Inc.,Operations,2023,6,2023-06,Software & Tools,7473.21,7139.45,-333.76,-4.47 +AcmeSaaS Inc.,Operations,2023,6,2023-06,Travel,7663.14,8303.19,640.05,8.35 +AcmeSaaS Inc.,Operations,2023,6,2023-06,Marketing Spend,7061.63,7434.07,372.44,5.27 +AcmeSaaS Inc.,Operations,2023,6,2023-06,Cloud Infrastructure,4477.43,4262.86,-214.57,-4.79 +AcmeSaaS Inc.,Operations,2023,6,2023-06,Contractors,1995.79,1904.44,-91.35,-4.58 +AcmeSaaS Inc.,Operations,2023,6,2023-06,Office & Facilities,7007.73,6853.66,-154.07,-2.2 +AcmeSaaS Inc.,Engineering,2023,7,2023-07,Salaries,11772.37,12098.74,326.37,2.77 +AcmeSaaS Inc.,Engineering,2023,7,2023-07,Software & Tools,9564.12,9107.22,-456.9,-4.78 +AcmeSaaS Inc.,Engineering,2023,7,2023-07,Travel,6081.08,6151.04,69.96,1.15 +AcmeSaaS Inc.,Engineering,2023,7,2023-07,Marketing Spend,12145.69,10689.39,-1456.3,-11.99 +AcmeSaaS Inc.,Engineering,2023,7,2023-07,Cloud Infrastructure,22901.3,21730.11,-1171.19,-5.11 +AcmeSaaS Inc.,Engineering,2023,7,2023-07,Contractors,17459.63,17165.84,-293.79,-1.68 +AcmeSaaS Inc.,Engineering,2023,7,2023-07,Office & Facilities,22124.33,22549.04,424.71,1.92 +AcmeSaaS Inc.,Sales,2023,7,2023-07,Salaries,12516.47,11524.24,-992.23,-7.93 +AcmeSaaS Inc.,Sales,2023,7,2023-07,Software & Tools,9625.46,8666.29,-959.17,-9.96 +AcmeSaaS Inc.,Sales,2023,7,2023-07,Travel,9277.59,9312.0,34.41,0.37 +AcmeSaaS Inc.,Sales,2023,7,2023-07,Marketing Spend,5796.23,5981.16,184.93,3.19 +AcmeSaaS Inc.,Sales,2023,7,2023-07,Cloud Infrastructure,9750.39,9364.72,-385.67,-3.96 +AcmeSaaS Inc.,Sales,2023,7,2023-07,Contractors,16272.38,17515.94,1243.56,7.64 +AcmeSaaS Inc.,Sales,2023,7,2023-07,Office & Facilities,14669.96,15554.16,884.2,6.03 +AcmeSaaS Inc.,Marketing,2023,7,2023-07,Salaries,13087.85,11746.07,-1341.78,-10.25 +AcmeSaaS Inc.,Marketing,2023,7,2023-07,Software & Tools,6100.78,5975.51,-125.27,-2.05 +AcmeSaaS Inc.,Marketing,2023,7,2023-07,Travel,5703.04,5880.65,177.61,3.11 +AcmeSaaS Inc.,Marketing,2023,7,2023-07,Marketing Spend,2979.27,2760.78,-218.49,-7.33 +AcmeSaaS Inc.,Marketing,2023,7,2023-07,Cloud Infrastructure,6415.74,6718.08,302.34,4.71 +AcmeSaaS Inc.,Marketing,2023,7,2023-07,Contractors,10006.19,9992.69,-13.5,-0.13 +AcmeSaaS Inc.,Marketing,2023,7,2023-07,Office & Facilities,15846.51,14872.84,-973.67,-6.14 +AcmeSaaS Inc.,Operations,2023,7,2023-07,Salaries,8509.09,9444.34,935.25,10.99 +AcmeSaaS Inc.,Operations,2023,7,2023-07,Software & Tools,1781.11,1788.79,7.68,0.43 +AcmeSaaS Inc.,Operations,2023,7,2023-07,Travel,9490.67,8466.18,-1024.49,-10.79 +AcmeSaaS Inc.,Operations,2023,7,2023-07,Marketing Spend,9688.03,9104.88,-583.15,-6.02 +AcmeSaaS Inc.,Operations,2023,7,2023-07,Cloud Infrastructure,2826.15,3062.42,236.27,8.36 +AcmeSaaS Inc.,Operations,2023,7,2023-07,Contractors,6120.87,6056.91,-63.96,-1.04 +AcmeSaaS Inc.,Operations,2023,7,2023-07,Office & Facilities,3542.89,3799.18,256.29,7.23 +AcmeSaaS Inc.,Engineering,2023,8,2023-08,Salaries,13071.65,13086.64,14.99,0.11 +AcmeSaaS Inc.,Engineering,2023,8,2023-08,Software & Tools,18090.61,19525.86,1435.25,7.93 +AcmeSaaS Inc.,Engineering,2023,8,2023-08,Travel,11941.53,12078.73,137.2,1.15 +AcmeSaaS Inc.,Engineering,2023,8,2023-08,Marketing Spend,17497.5,19165.54,1668.04,9.53 +AcmeSaaS Inc.,Engineering,2023,8,2023-08,Cloud Infrastructure,16579.09,17548.59,969.5,5.85 +AcmeSaaS Inc.,Engineering,2023,8,2023-08,Contractors,12211.03,12136.81,-74.22,-0.61 +AcmeSaaS Inc.,Engineering,2023,8,2023-08,Office & Facilities,13881.67,13079.39,-802.28,-5.78 +AcmeSaaS Inc.,Sales,2023,8,2023-08,Salaries,7603.28,7212.28,-391.0,-5.14 +AcmeSaaS Inc.,Sales,2023,8,2023-08,Software & Tools,14775.16,13965.65,-809.51,-5.48 +AcmeSaaS Inc.,Sales,2023,8,2023-08,Travel,17129.27,16388.09,-741.18,-4.33 +AcmeSaaS Inc.,Sales,2023,8,2023-08,Marketing Spend,12637.65,12759.43,121.78,0.96 +AcmeSaaS Inc.,Sales,2023,8,2023-08,Cloud Infrastructure,14574.7,13309.76,-1264.94,-8.68 +AcmeSaaS Inc.,Sales,2023,8,2023-08,Contractors,8105.83,7583.03,-522.8,-6.45 +AcmeSaaS Inc.,Sales,2023,8,2023-08,Office & Facilities,4484.93,4693.69,208.76,4.65 +AcmeSaaS Inc.,Marketing,2023,8,2023-08,Salaries,13559.97,14877.47,1317.5,9.72 +AcmeSaaS Inc.,Marketing,2023,8,2023-08,Software & Tools,3586.06,3658.42,72.36,2.02 +AcmeSaaS Inc.,Marketing,2023,8,2023-08,Travel,8918.97,9337.5,418.53,4.69 +AcmeSaaS Inc.,Marketing,2023,8,2023-08,Marketing Spend,11015.85,11958.98,943.13,8.56 +AcmeSaaS Inc.,Marketing,2023,8,2023-08,Cloud Infrastructure,9045.94,9622.55,576.61,6.37 +AcmeSaaS Inc.,Marketing,2023,8,2023-08,Contractors,5800.88,5634.35,-166.53,-2.87 +AcmeSaaS Inc.,Marketing,2023,8,2023-08,Office & Facilities,9113.8,8033.04,-1080.76,-11.86 +AcmeSaaS Inc.,Operations,2023,8,2023-08,Salaries,3786.02,3879.84,93.82,2.48 +AcmeSaaS Inc.,Operations,2023,8,2023-08,Software & Tools,6719.72,6269.02,-450.7,-6.71 +AcmeSaaS Inc.,Operations,2023,8,2023-08,Travel,7449.82,6948.16,-501.66,-6.73 +AcmeSaaS Inc.,Operations,2023,8,2023-08,Marketing Spend,8179.98,8054.01,-125.97,-1.54 +AcmeSaaS Inc.,Operations,2023,8,2023-08,Cloud Infrastructure,4277.23,3793.76,-483.47,-11.3 +AcmeSaaS Inc.,Operations,2023,8,2023-08,Contractors,6676.2,6413.63,-262.57,-3.93 +AcmeSaaS Inc.,Operations,2023,8,2023-08,Office & Facilities,5205.51,5429.32,223.81,4.3 +AcmeSaaS Inc.,Engineering,2023,9,2023-09,Salaries,17685.15,17958.46,273.31,1.55 +AcmeSaaS Inc.,Engineering,2023,9,2023-09,Software & Tools,10274.15,9108.08,-1166.07,-11.35 +AcmeSaaS Inc.,Engineering,2023,9,2023-09,Travel,19638.74,20311.56,672.82,3.43 +AcmeSaaS Inc.,Engineering,2023,9,2023-09,Marketing Spend,9115.23,8318.27,-796.96,-8.74 +AcmeSaaS Inc.,Engineering,2023,9,2023-09,Cloud Infrastructure,24435.45,24210.83,-224.62,-0.92 +AcmeSaaS Inc.,Engineering,2023,9,2023-09,Contractors,5997.43,5350.12,-647.31,-10.79 +AcmeSaaS Inc.,Engineering,2023,9,2023-09,Office & Facilities,17366.24,16862.36,-503.88,-2.9 +AcmeSaaS Inc.,Sales,2023,9,2023-09,Salaries,6524.4,5869.73,-654.67,-10.03 +AcmeSaaS Inc.,Sales,2023,9,2023-09,Software & Tools,8510.82,7529.11,-981.71,-11.53 +AcmeSaaS Inc.,Sales,2023,9,2023-09,Travel,16001.95,16153.34,151.39,0.95 +AcmeSaaS Inc.,Sales,2023,9,2023-09,Marketing Spend,9412.42,10541.7,1129.28,12.0 +AcmeSaaS Inc.,Sales,2023,9,2023-09,Cloud Infrastructure,15842.95,15272.45,-570.5,-3.6 +AcmeSaaS Inc.,Sales,2023,9,2023-09,Contractors,17221.11,17841.67,620.56,3.6 +AcmeSaaS Inc.,Sales,2023,9,2023-09,Office & Facilities,7224.76,7712.4,487.64,6.75 +AcmeSaaS Inc.,Marketing,2023,9,2023-09,Salaries,12611.77,13124.69,512.92,4.07 +AcmeSaaS Inc.,Marketing,2023,9,2023-09,Software & Tools,14190.95,14408.82,217.87,1.54 +AcmeSaaS Inc.,Marketing,2023,9,2023-09,Travel,17201.71,16037.35,-1164.36,-6.77 +AcmeSaaS Inc.,Marketing,2023,9,2023-09,Marketing Spend,5640.43,5910.45,270.02,4.79 +AcmeSaaS Inc.,Marketing,2023,9,2023-09,Cloud Infrastructure,2882.37,3067.0,184.63,6.41 +AcmeSaaS Inc.,Marketing,2023,9,2023-09,Contractors,4916.5,4524.5,-392.0,-7.97 +AcmeSaaS Inc.,Marketing,2023,9,2023-09,Office & Facilities,4513.36,4629.53,116.17,2.57 +AcmeSaaS Inc.,Operations,2023,9,2023-09,Salaries,9155.38,9545.06,389.68,4.26 +AcmeSaaS Inc.,Operations,2023,9,2023-09,Software & Tools,2814.9,3124.43,309.53,11.0 +AcmeSaaS Inc.,Operations,2023,9,2023-09,Travel,9869.87,9625.07,-244.8,-2.48 +AcmeSaaS Inc.,Operations,2023,9,2023-09,Marketing Spend,11325.57,11910.01,584.44,5.16 +AcmeSaaS Inc.,Operations,2023,9,2023-09,Cloud Infrastructure,2750.49,2470.6,-279.89,-10.18 +AcmeSaaS Inc.,Operations,2023,9,2023-09,Contractors,1925.44,2013.52,88.08,4.57 +AcmeSaaS Inc.,Operations,2023,9,2023-09,Office & Facilities,4791.19,4937.5,146.31,3.05 +AcmeSaaS Inc.,Engineering,2023,10,2023-10,Salaries,5280.53,5086.89,-193.64,-3.67 +AcmeSaaS Inc.,Engineering,2023,10,2023-10,Software & Tools,18465.34,18147.93,-317.41,-1.72 +AcmeSaaS Inc.,Engineering,2023,10,2023-10,Travel,19995.27,19374.16,-621.11,-3.11 +AcmeSaaS Inc.,Engineering,2023,10,2023-10,Marketing Spend,15082.14,15103.72,21.58,0.14 +AcmeSaaS Inc.,Engineering,2023,10,2023-10,Cloud Infrastructure,5657.13,5441.57,-215.56,-3.81 +AcmeSaaS Inc.,Engineering,2023,10,2023-10,Contractors,22621.12,24518.99,1897.87,8.39 +AcmeSaaS Inc.,Engineering,2023,10,2023-10,Office & Facilities,18664.99,20108.9,1443.91,7.74 +AcmeSaaS Inc.,Sales,2023,10,2023-10,Salaries,4013.91,4462.32,448.41,11.17 +AcmeSaaS Inc.,Sales,2023,10,2023-10,Software & Tools,16625.3,15707.91,-917.39,-5.52 +AcmeSaaS Inc.,Sales,2023,10,2023-10,Travel,11829.91,12704.94,875.03,7.4 +AcmeSaaS Inc.,Sales,2023,10,2023-10,Marketing Spend,14677.67,14812.14,134.47,0.92 +AcmeSaaS Inc.,Sales,2023,10,2023-10,Cloud Infrastructure,12887.53,12836.49,-51.04,-0.4 +AcmeSaaS Inc.,Sales,2023,10,2023-10,Contractors,8879.29,8742.0,-137.29,-1.55 +AcmeSaaS Inc.,Sales,2023,10,2023-10,Office & Facilities,13278.1,14014.32,736.22,5.54 +AcmeSaaS Inc.,Marketing,2023,10,2023-10,Salaries,5706.59,5857.7,151.11,2.65 +AcmeSaaS Inc.,Marketing,2023,10,2023-10,Software & Tools,13357.8,12969.86,-387.94,-2.9 +AcmeSaaS Inc.,Marketing,2023,10,2023-10,Travel,13082.59,11602.79,-1479.8,-11.31 +AcmeSaaS Inc.,Marketing,2023,10,2023-10,Marketing Spend,3322.85,3602.73,279.88,8.42 +AcmeSaaS Inc.,Marketing,2023,10,2023-10,Cloud Infrastructure,13750.23,12700.28,-1049.95,-7.64 +AcmeSaaS Inc.,Marketing,2023,10,2023-10,Contractors,5384.81,5012.77,-372.04,-6.91 +AcmeSaaS Inc.,Marketing,2023,10,2023-10,Office & Facilities,8281.57,8873.53,591.96,7.15 +AcmeSaaS Inc.,Operations,2023,10,2023-10,Salaries,4942.15,5203.18,261.03,5.28 +AcmeSaaS Inc.,Operations,2023,10,2023-10,Software & Tools,10205.75,10177.77,-27.98,-0.27 +AcmeSaaS Inc.,Operations,2023,10,2023-10,Travel,8459.57,8983.72,524.15,6.2 +AcmeSaaS Inc.,Operations,2023,10,2023-10,Marketing Spend,4317.61,4515.12,197.51,4.57 +AcmeSaaS Inc.,Operations,2023,10,2023-10,Cloud Infrastructure,1723.57,1783.92,60.35,3.5 +AcmeSaaS Inc.,Operations,2023,10,2023-10,Contractors,10866.08,10842.14,-23.94,-0.22 +AcmeSaaS Inc.,Operations,2023,10,2023-10,Office & Facilities,2459.15,2632.04,172.89,7.03 +AcmeSaaS Inc.,Engineering,2023,11,2023-11,Salaries,6838.62,6716.35,-122.27,-1.79 +AcmeSaaS Inc.,Engineering,2023,11,2023-11,Software & Tools,10223.25,10826.67,603.42,5.9 +AcmeSaaS Inc.,Engineering,2023,11,2023-11,Travel,22603.73,21685.79,-917.94,-4.06 +AcmeSaaS Inc.,Engineering,2023,11,2023-11,Marketing Spend,12451.09,13057.27,606.18,4.87 +AcmeSaaS Inc.,Engineering,2023,11,2023-11,Cloud Infrastructure,19701.25,18618.07,-1083.18,-5.5 +AcmeSaaS Inc.,Engineering,2023,11,2023-11,Contractors,16849.76,15844.45,-1005.31,-5.97 +AcmeSaaS Inc.,Engineering,2023,11,2023-11,Office & Facilities,18368.03,16695.76,-1672.27,-9.1 +AcmeSaaS Inc.,Sales,2023,11,2023-11,Salaries,8207.01,8649.37,442.36,5.39 +AcmeSaaS Inc.,Sales,2023,11,2023-11,Software & Tools,6538.67,7286.6,747.93,11.44 +AcmeSaaS Inc.,Sales,2023,11,2023-11,Travel,16049.17,16144.07,94.9,0.59 +AcmeSaaS Inc.,Sales,2023,11,2023-11,Marketing Spend,21219.53,20114.41,-1105.12,-5.21 +AcmeSaaS Inc.,Sales,2023,11,2023-11,Cloud Infrastructure,8037.18,7266.63,-770.55,-9.59 +AcmeSaaS Inc.,Sales,2023,11,2023-11,Contractors,8750.73,8108.32,-642.41,-7.34 +AcmeSaaS Inc.,Sales,2023,11,2023-11,Office & Facilities,14868.88,13896.4,-972.48,-6.54 +AcmeSaaS Inc.,Marketing,2023,11,2023-11,Salaries,5028.02,4577.04,-450.98,-8.97 +AcmeSaaS Inc.,Marketing,2023,11,2023-11,Software & Tools,2626.76,2859.05,232.29,8.84 +AcmeSaaS Inc.,Marketing,2023,11,2023-11,Travel,10180.77,10158.48,-22.29,-0.22 +AcmeSaaS Inc.,Marketing,2023,11,2023-11,Marketing Spend,6406.23,6979.28,573.05,8.95 +AcmeSaaS Inc.,Marketing,2023,11,2023-11,Cloud Infrastructure,16575.11,16869.74,294.63,1.78 +AcmeSaaS Inc.,Marketing,2023,11,2023-11,Contractors,10460.04,10383.21,-76.83,-0.73 +AcmeSaaS Inc.,Marketing,2023,11,2023-11,Office & Facilities,12552.82,12373.47,-179.35,-1.43 +AcmeSaaS Inc.,Operations,2023,11,2023-11,Salaries,3692.43,3807.14,114.71,3.11 +AcmeSaaS Inc.,Operations,2023,11,2023-11,Software & Tools,2293.56,2047.84,-245.72,-10.71 +AcmeSaaS Inc.,Operations,2023,11,2023-11,Travel,11652.04,10671.02,-981.02,-8.42 +AcmeSaaS Inc.,Operations,2023,11,2023-11,Marketing Spend,6778.3,6880.53,102.23,1.51 +AcmeSaaS Inc.,Operations,2023,11,2023-11,Cloud Infrastructure,10400.84,9911.17,-489.67,-4.71 +AcmeSaaS Inc.,Operations,2023,11,2023-11,Contractors,5968.12,6675.58,707.46,11.85 +AcmeSaaS Inc.,Operations,2023,11,2023-11,Office & Facilities,2532.4,2300.5,-231.9,-9.16 +AcmeSaaS Inc.,Engineering,2023,12,2023-12,Salaries,20294.73,22049.01,1754.28,8.64 +AcmeSaaS Inc.,Engineering,2023,12,2023-12,Software & Tools,16848.18,18829.65,1981.47,11.76 +AcmeSaaS Inc.,Engineering,2023,12,2023-12,Travel,20867.92,19893.21,-974.71,-4.67 +AcmeSaaS Inc.,Engineering,2023,12,2023-12,Marketing Spend,8551.85,8800.25,248.4,2.9 +AcmeSaaS Inc.,Engineering,2023,12,2023-12,Cloud Infrastructure,15022.85,15418.12,395.27,2.63 +AcmeSaaS Inc.,Engineering,2023,12,2023-12,Contractors,13452.25,14227.39,775.14,5.76 +AcmeSaaS Inc.,Engineering,2023,12,2023-12,Office & Facilities,13282.38,14709.2,1426.82,10.74 +AcmeSaaS Inc.,Sales,2023,12,2023-12,Salaries,12015.36,11872.63,-142.73,-1.19 +AcmeSaaS Inc.,Sales,2023,12,2023-12,Software & Tools,12119.24,12392.1,272.86,2.25 +AcmeSaaS Inc.,Sales,2023,12,2023-12,Travel,26539.52,25209.95,-1329.57,-5.01 +AcmeSaaS Inc.,Sales,2023,12,2023-12,Marketing Spend,10387.53,9718.1,-669.43,-6.44 +AcmeSaaS Inc.,Sales,2023,12,2023-12,Cloud Infrastructure,10925.21,11467.86,542.65,4.97 +AcmeSaaS Inc.,Sales,2023,12,2023-12,Contractors,7756.59,8134.47,377.88,4.87 +AcmeSaaS Inc.,Sales,2023,12,2023-12,Office & Facilities,5433.8,5373.85,-59.95,-1.1 +AcmeSaaS Inc.,Marketing,2023,12,2023-12,Salaries,8908.88,9004.16,95.28,1.07 +AcmeSaaS Inc.,Marketing,2023,12,2023-12,Software & Tools,11376.15,11779.23,403.08,3.54 +AcmeSaaS Inc.,Marketing,2023,12,2023-12,Travel,9956.63,10932.57,975.94,9.8 +AcmeSaaS Inc.,Marketing,2023,12,2023-12,Marketing Spend,8258.73,8906.14,647.41,7.84 +AcmeSaaS Inc.,Marketing,2023,12,2023-12,Cloud Infrastructure,8635.56,7747.29,-888.27,-10.29 +AcmeSaaS Inc.,Marketing,2023,12,2023-12,Contractors,11477.94,10557.66,-920.28,-8.02 +AcmeSaaS Inc.,Marketing,2023,12,2023-12,Office & Facilities,6173.31,5888.27,-285.04,-4.62 +AcmeSaaS Inc.,Operations,2023,12,2023-12,Salaries,7646.0,7646.87,0.87,0.01 +AcmeSaaS Inc.,Operations,2023,12,2023-12,Software & Tools,6144.98,6135.83,-9.15,-0.15 +AcmeSaaS Inc.,Operations,2023,12,2023-12,Travel,3801.83,3419.01,-382.82,-10.07 +AcmeSaaS Inc.,Operations,2023,12,2023-12,Marketing Spend,2430.19,2161.82,-268.37,-11.04 +AcmeSaaS Inc.,Operations,2023,12,2023-12,Cloud Infrastructure,7142.63,7026.11,-116.52,-1.63 +AcmeSaaS Inc.,Operations,2023,12,2023-12,Contractors,7234.95,6926.43,-308.52,-4.26 +AcmeSaaS Inc.,Operations,2023,12,2023-12,Office & Facilities,9263.66,8708.66,-555.0,-5.99 +AcmeSaaS Inc.,Engineering,2024,1,2024-01,Salaries,4522.32,4272.16,-250.16,-5.53 +AcmeSaaS Inc.,Engineering,2024,1,2024-01,Software & Tools,19782.63,17599.73,-2182.9,-11.03 +AcmeSaaS Inc.,Engineering,2024,1,2024-01,Travel,17574.83,18655.76,1080.93,6.15 +AcmeSaaS Inc.,Engineering,2024,1,2024-01,Marketing Spend,13004.03,12911.96,-92.07,-0.71 +AcmeSaaS Inc.,Engineering,2024,1,2024-01,Cloud Infrastructure,19587.62,20299.87,712.25,3.64 +AcmeSaaS Inc.,Engineering,2024,1,2024-01,Contractors,20442.79,22484.16,2041.37,9.99 +AcmeSaaS Inc.,Engineering,2024,1,2024-01,Office & Facilities,14705.77,13581.62,-1124.15,-7.64 +AcmeSaaS Inc.,Sales,2024,1,2024-01,Salaries,15089.94,16385.5,1295.56,8.59 +AcmeSaaS Inc.,Sales,2024,1,2024-01,Software & Tools,16082.33,15425.41,-656.92,-4.08 +AcmeSaaS Inc.,Sales,2024,1,2024-01,Travel,13211.64,13825.74,614.1,4.65 +AcmeSaaS Inc.,Sales,2024,1,2024-01,Marketing Spend,5175.33,4912.28,-263.05,-5.08 +AcmeSaaS Inc.,Sales,2024,1,2024-01,Cloud Infrastructure,10326.78,11430.16,1103.38,10.68 +AcmeSaaS Inc.,Sales,2024,1,2024-01,Contractors,10032.75,10787.77,755.02,7.53 +AcmeSaaS Inc.,Sales,2024,1,2024-01,Office & Facilities,16791.66,16993.55,201.89,1.2 +AcmeSaaS Inc.,Marketing,2024,1,2024-01,Salaries,7956.75,8257.82,301.07,3.78 +AcmeSaaS Inc.,Marketing,2024,1,2024-01,Software & Tools,6160.43,6223.4,62.97,1.02 +AcmeSaaS Inc.,Marketing,2024,1,2024-01,Travel,6272.54,6141.94,-130.6,-2.08 +AcmeSaaS Inc.,Marketing,2024,1,2024-01,Marketing Spend,14554.71,13463.4,-1091.31,-7.5 +AcmeSaaS Inc.,Marketing,2024,1,2024-01,Cloud Infrastructure,7308.29,7065.85,-242.44,-3.32 +AcmeSaaS Inc.,Marketing,2024,1,2024-01,Contractors,8721.97,9258.78,536.81,6.15 +AcmeSaaS Inc.,Marketing,2024,1,2024-01,Office & Facilities,14784.32,15229.3,444.98,3.01 +AcmeSaaS Inc.,Operations,2024,1,2024-01,Salaries,8383.62,9335.62,952.0,11.36 +AcmeSaaS Inc.,Operations,2024,1,2024-01,Software & Tools,3349.49,3437.01,87.52,2.61 +AcmeSaaS Inc.,Operations,2024,1,2024-01,Travel,6476.74,6071.5,-405.24,-6.26 +AcmeSaaS Inc.,Operations,2024,1,2024-01,Marketing Spend,9900.67,9088.92,-811.75,-8.2 +AcmeSaaS Inc.,Operations,2024,1,2024-01,Cloud Infrastructure,5471.57,5538.33,66.76,1.22 +AcmeSaaS Inc.,Operations,2024,1,2024-01,Contractors,7825.04,7923.17,98.13,1.25 +AcmeSaaS Inc.,Operations,2024,1,2024-01,Office & Facilities,2606.42,2351.96,-254.46,-9.76 +AcmeSaaS Inc.,Engineering,2024,2,2024-02,Salaries,22563.74,22611.78,48.04,0.21 +AcmeSaaS Inc.,Engineering,2024,2,2024-02,Software & Tools,21019.27,19876.28,-1142.99,-5.44 +AcmeSaaS Inc.,Engineering,2024,2,2024-02,Travel,12229.12,13211.53,982.41,8.03 +AcmeSaaS Inc.,Engineering,2024,2,2024-02,Marketing Spend,5531.94,6169.54,637.6,11.53 +AcmeSaaS Inc.,Engineering,2024,2,2024-02,Cloud Infrastructure,19446.39,18250.35,-1196.04,-6.15 +AcmeSaaS Inc.,Engineering,2024,2,2024-02,Contractors,12948.08,13107.39,159.31,1.23 +AcmeSaaS Inc.,Engineering,2024,2,2024-02,Office & Facilities,17196.88,16716.41,-480.47,-2.79 +AcmeSaaS Inc.,Sales,2024,2,2024-02,Salaries,16505.92,18226.16,1720.24,10.42 +AcmeSaaS Inc.,Sales,2024,2,2024-02,Software & Tools,10233.91,10252.91,19.0,0.19 +AcmeSaaS Inc.,Sales,2024,2,2024-02,Travel,15860.84,17081.04,1220.2,7.69 +AcmeSaaS Inc.,Sales,2024,2,2024-02,Marketing Spend,15628.85,14814.3,-814.55,-5.21 +AcmeSaaS Inc.,Sales,2024,2,2024-02,Cloud Infrastructure,6716.09,6391.39,-324.7,-4.83 +AcmeSaaS Inc.,Sales,2024,2,2024-02,Contractors,14506.44,14809.12,302.68,2.09 +AcmeSaaS Inc.,Sales,2024,2,2024-02,Office & Facilities,8819.19,9875.17,1055.98,11.97 +AcmeSaaS Inc.,Marketing,2024,2,2024-02,Salaries,10333.19,9891.2,-441.99,-4.28 +AcmeSaaS Inc.,Marketing,2024,2,2024-02,Software & Tools,4963.62,4592.72,-370.9,-7.47 +AcmeSaaS Inc.,Marketing,2024,2,2024-02,Travel,11103.72,11630.03,526.31,4.74 +AcmeSaaS Inc.,Marketing,2024,2,2024-02,Marketing Spend,8057.86,8196.71,138.85,1.72 +AcmeSaaS Inc.,Marketing,2024,2,2024-02,Cloud Infrastructure,11313.7,10590.25,-723.45,-6.39 +AcmeSaaS Inc.,Marketing,2024,2,2024-02,Contractors,11180.07,11919.42,739.35,6.61 +AcmeSaaS Inc.,Marketing,2024,2,2024-02,Office & Facilities,9793.22,8720.62,-1072.6,-10.95 +AcmeSaaS Inc.,Operations,2024,2,2024-02,Salaries,6439.15,6431.93,-7.22,-0.11 +AcmeSaaS Inc.,Operations,2024,2,2024-02,Software & Tools,6160.23,5475.73,-684.5,-11.11 +AcmeSaaS Inc.,Operations,2024,2,2024-02,Travel,6910.43,6914.23,3.8,0.05 +AcmeSaaS Inc.,Operations,2024,2,2024-02,Marketing Spend,3905.33,3989.85,84.52,2.16 +AcmeSaaS Inc.,Operations,2024,2,2024-02,Cloud Infrastructure,5866.74,6387.28,520.54,8.87 +AcmeSaaS Inc.,Operations,2024,2,2024-02,Contractors,6976.41,7602.93,626.52,8.98 +AcmeSaaS Inc.,Operations,2024,2,2024-02,Office & Facilities,8107.36,7991.21,-116.15,-1.43 +AcmeSaaS Inc.,Engineering,2024,3,2024-03,Salaries,17049.92,18969.89,1919.97,11.26 +AcmeSaaS Inc.,Engineering,2024,3,2024-03,Software & Tools,15350.8,14756.03,-594.77,-3.87 +AcmeSaaS Inc.,Engineering,2024,3,2024-03,Travel,21886.91,22899.16,1012.25,4.62 +AcmeSaaS Inc.,Engineering,2024,3,2024-03,Marketing Spend,14195.07,14705.54,510.47,3.6 +AcmeSaaS Inc.,Engineering,2024,3,2024-03,Cloud Infrastructure,20221.28,21928.43,1707.15,8.44 +AcmeSaaS Inc.,Engineering,2024,3,2024-03,Contractors,7902.63,8570.89,668.26,8.46 +AcmeSaaS Inc.,Engineering,2024,3,2024-03,Office & Facilities,15660.05,17010.61,1350.56,8.62 +AcmeSaaS Inc.,Sales,2024,3,2024-03,Salaries,11376.23,11734.34,358.11,3.15 +AcmeSaaS Inc.,Sales,2024,3,2024-03,Software & Tools,10057.96,11074.05,1016.09,10.1 +AcmeSaaS Inc.,Sales,2024,3,2024-03,Travel,18424.68,20624.26,2199.58,11.94 +AcmeSaaS Inc.,Sales,2024,3,2024-03,Marketing Spend,19271.32,20412.64,1141.32,5.92 +AcmeSaaS Inc.,Sales,2024,3,2024-03,Cloud Infrastructure,21622.43,21279.78,-342.65,-1.58 +AcmeSaaS Inc.,Sales,2024,3,2024-03,Contractors,4215.36,3809.11,-406.25,-9.64 +AcmeSaaS Inc.,Sales,2024,3,2024-03,Office & Facilities,4892.13,5049.16,157.03,3.21 +AcmeSaaS Inc.,Marketing,2024,3,2024-03,Salaries,13498.34,13092.87,-405.47,-3.0 +AcmeSaaS Inc.,Marketing,2024,3,2024-03,Software & Tools,7927.52,7253.18,-674.34,-8.51 +AcmeSaaS Inc.,Marketing,2024,3,2024-03,Travel,11178.86,11262.48,83.62,0.75 +AcmeSaaS Inc.,Marketing,2024,3,2024-03,Marketing Spend,13898.97,14118.89,219.92,1.58 +AcmeSaaS Inc.,Marketing,2024,3,2024-03,Cloud Infrastructure,2762.12,2956.03,193.91,7.02 +AcmeSaaS Inc.,Marketing,2024,3,2024-03,Contractors,12505.55,11515.06,-990.49,-7.92 +AcmeSaaS Inc.,Marketing,2024,3,2024-03,Office & Facilities,5975.2,5371.42,-603.78,-10.1 +AcmeSaaS Inc.,Operations,2024,3,2024-03,Salaries,9937.18,9353.65,-583.53,-5.87 +AcmeSaaS Inc.,Operations,2024,3,2024-03,Software & Tools,7531.88,6645.04,-886.84,-11.77 +AcmeSaaS Inc.,Operations,2024,3,2024-03,Travel,3902.98,4188.33,285.35,7.31 +AcmeSaaS Inc.,Operations,2024,3,2024-03,Marketing Spend,10339.35,11334.93,995.58,9.63 +AcmeSaaS Inc.,Operations,2024,3,2024-03,Cloud Infrastructure,2967.1,3093.58,126.48,4.26 +AcmeSaaS Inc.,Operations,2024,3,2024-03,Contractors,6013.19,5519.59,-493.6,-8.21 +AcmeSaaS Inc.,Operations,2024,3,2024-03,Office & Facilities,4028.91,3972.57,-56.34,-1.4 +AcmeSaaS Inc.,Engineering,2024,4,2024-04,Salaries,13055.4,12694.11,-361.29,-2.77 +AcmeSaaS Inc.,Engineering,2024,4,2024-04,Software & Tools,19223.48,19146.01,-77.47,-0.4 +AcmeSaaS Inc.,Engineering,2024,4,2024-04,Travel,20532.68,19237.67,-1295.01,-6.31 +AcmeSaaS Inc.,Engineering,2024,4,2024-04,Marketing Spend,15062.35,15322.35,260.0,1.73 +AcmeSaaS Inc.,Engineering,2024,4,2024-04,Cloud Infrastructure,10622.2,10812.92,190.72,1.8 +AcmeSaaS Inc.,Engineering,2024,4,2024-04,Contractors,25792.37,28842.22,3049.85,11.82 +AcmeSaaS Inc.,Engineering,2024,4,2024-04,Office & Facilities,9325.38,8867.09,-458.29,-4.91 +AcmeSaaS Inc.,Sales,2024,4,2024-04,Salaries,20439.41,20961.38,521.97,2.55 +AcmeSaaS Inc.,Sales,2024,4,2024-04,Software & Tools,14730.24,14718.67,-11.57,-0.08 +AcmeSaaS Inc.,Sales,2024,4,2024-04,Travel,7877.6,8641.71,764.11,9.7 +AcmeSaaS Inc.,Sales,2024,4,2024-04,Marketing Spend,13082.02,12410.74,-671.28,-5.13 +AcmeSaaS Inc.,Sales,2024,4,2024-04,Cloud Infrastructure,15222.56,16314.42,1091.86,7.17 +AcmeSaaS Inc.,Sales,2024,4,2024-04,Contractors,16273.79,16691.95,418.16,2.57 +AcmeSaaS Inc.,Sales,2024,4,2024-04,Office & Facilities,3851.97,3715.44,-136.53,-3.54 +AcmeSaaS Inc.,Marketing,2024,4,2024-04,Salaries,9285.5,10184.49,898.99,9.68 +AcmeSaaS Inc.,Marketing,2024,4,2024-04,Software & Tools,9103.71,9423.45,319.74,3.51 +AcmeSaaS Inc.,Marketing,2024,4,2024-04,Travel,9761.13,9313.52,-447.61,-4.59 +AcmeSaaS Inc.,Marketing,2024,4,2024-04,Marketing Spend,10260.08,10114.36,-145.72,-1.42 +AcmeSaaS Inc.,Marketing,2024,4,2024-04,Cloud Infrastructure,9546.32,9728.63,182.31,1.91 +AcmeSaaS Inc.,Marketing,2024,4,2024-04,Contractors,11617.26,12265.11,647.85,5.58 +AcmeSaaS Inc.,Marketing,2024,4,2024-04,Office & Facilities,9188.75,8284.87,-903.88,-9.84 +AcmeSaaS Inc.,Operations,2024,4,2024-04,Salaries,4566.14,5019.27,453.13,9.92 +AcmeSaaS Inc.,Operations,2024,4,2024-04,Software & Tools,9039.26,9756.19,716.93,7.93 +AcmeSaaS Inc.,Operations,2024,4,2024-04,Travel,3384.79,3187.36,-197.43,-5.83 +AcmeSaaS Inc.,Operations,2024,4,2024-04,Marketing Spend,2954.85,3185.11,230.26,7.79 +AcmeSaaS Inc.,Operations,2024,4,2024-04,Cloud Infrastructure,6981.8,6951.38,-30.42,-0.44 +AcmeSaaS Inc.,Operations,2024,4,2024-04,Contractors,11254.3,12082.14,827.84,7.36 +AcmeSaaS Inc.,Operations,2024,4,2024-04,Office & Facilities,6897.2,7305.34,408.14,5.92 +AcmeSaaS Inc.,Engineering,2024,5,2024-05,Salaries,11015.5,12284.33,1268.83,11.52 +AcmeSaaS Inc.,Engineering,2024,5,2024-05,Software & Tools,6143.01,6831.92,688.91,11.21 +AcmeSaaS Inc.,Engineering,2024,5,2024-05,Travel,24620.32,26420.09,1799.77,7.31 +AcmeSaaS Inc.,Engineering,2024,5,2024-05,Marketing Spend,6700.72,6484.86,-215.86,-3.22 +AcmeSaaS Inc.,Engineering,2024,5,2024-05,Cloud Infrastructure,24698.94,26422.03,1723.09,6.98 +AcmeSaaS Inc.,Engineering,2024,5,2024-05,Contractors,22380.69,19769.77,-2610.92,-11.67 +AcmeSaaS Inc.,Engineering,2024,5,2024-05,Office & Facilities,19418.04,19588.48,170.44,0.88 +AcmeSaaS Inc.,Sales,2024,5,2024-05,Salaries,10673.14,9991.31,-681.83,-6.39 +AcmeSaaS Inc.,Sales,2024,5,2024-05,Software & Tools,14417.9,12774.34,-1643.56,-11.4 +AcmeSaaS Inc.,Sales,2024,5,2024-05,Travel,14409.53,15738.32,1328.79,9.22 +AcmeSaaS Inc.,Sales,2024,5,2024-05,Marketing Spend,12901.94,13092.09,190.15,1.47 +AcmeSaaS Inc.,Sales,2024,5,2024-05,Cloud Infrastructure,16987.02,18679.97,1692.95,9.97 +AcmeSaaS Inc.,Sales,2024,5,2024-05,Contractors,19011.45,17740.12,-1271.33,-6.69 +AcmeSaaS Inc.,Sales,2024,5,2024-05,Office & Facilities,4723.21,4228.09,-495.12,-10.48 +AcmeSaaS Inc.,Marketing,2024,5,2024-05,Salaries,13824.33,13799.86,-24.47,-0.18 +AcmeSaaS Inc.,Marketing,2024,5,2024-05,Software & Tools,15018.07,13566.21,-1451.86,-9.67 +AcmeSaaS Inc.,Marketing,2024,5,2024-05,Travel,6543.65,7151.83,608.18,9.29 +AcmeSaaS Inc.,Marketing,2024,5,2024-05,Marketing Spend,8024.53,7322.86,-701.67,-8.74 +AcmeSaaS Inc.,Marketing,2024,5,2024-05,Cloud Infrastructure,4276.92,4229.34,-47.58,-1.11 +AcmeSaaS Inc.,Marketing,2024,5,2024-05,Contractors,15532.71,16168.26,635.55,4.09 +AcmeSaaS Inc.,Marketing,2024,5,2024-05,Office & Facilities,6574.0,6957.62,383.62,5.84 +AcmeSaaS Inc.,Operations,2024,5,2024-05,Salaries,11407.8,11156.23,-251.57,-2.21 +AcmeSaaS Inc.,Operations,2024,5,2024-05,Software & Tools,6006.09,6656.94,650.85,10.84 +AcmeSaaS Inc.,Operations,2024,5,2024-05,Travel,9319.24,8274.11,-1045.13,-11.21 +AcmeSaaS Inc.,Operations,2024,5,2024-05,Marketing Spend,3293.13,3190.8,-102.33,-3.11 +AcmeSaaS Inc.,Operations,2024,5,2024-05,Cloud Infrastructure,5962.59,5881.57,-81.02,-1.36 +AcmeSaaS Inc.,Operations,2024,5,2024-05,Contractors,2724.08,3018.64,294.56,10.81 +AcmeSaaS Inc.,Operations,2024,5,2024-05,Office & Facilities,6726.05,7299.84,573.79,8.53 +AcmeSaaS Inc.,Engineering,2024,6,2024-06,Salaries,7002.04,6367.08,-634.96,-9.07 +AcmeSaaS Inc.,Engineering,2024,6,2024-06,Software & Tools,22434.91,24308.85,1873.94,8.35 +AcmeSaaS Inc.,Engineering,2024,6,2024-06,Travel,18717.96,18514.54,-203.42,-1.09 +AcmeSaaS Inc.,Engineering,2024,6,2024-06,Marketing Spend,30125.01,31301.83,1176.82,3.91 +AcmeSaaS Inc.,Engineering,2024,6,2024-06,Cloud Infrastructure,13827.66,14297.93,470.27,3.4 +AcmeSaaS Inc.,Engineering,2024,6,2024-06,Contractors,14866.46,15213.07,346.61,2.33 +AcmeSaaS Inc.,Engineering,2024,6,2024-06,Office & Facilities,9382.9,8305.05,-1077.85,-11.49 +AcmeSaaS Inc.,Sales,2024,6,2024-06,Salaries,23008.66,21440.12,-1568.54,-6.82 +AcmeSaaS Inc.,Sales,2024,6,2024-06,Software & Tools,9899.69,10778.06,878.37,8.87 +AcmeSaaS Inc.,Sales,2024,6,2024-06,Travel,7060.71,6770.19,-290.52,-4.11 +AcmeSaaS Inc.,Sales,2024,6,2024-06,Marketing Spend,17646.19,16153.55,-1492.64,-8.46 +AcmeSaaS Inc.,Sales,2024,6,2024-06,Cloud Infrastructure,5677.63,6223.41,545.78,9.61 +AcmeSaaS Inc.,Sales,2024,6,2024-06,Contractors,22486.52,19803.44,-2683.08,-11.93 +AcmeSaaS Inc.,Sales,2024,6,2024-06,Office & Facilities,9021.02,9796.99,775.97,8.6 +AcmeSaaS Inc.,Marketing,2024,6,2024-06,Salaries,8588.41,9186.13,597.72,6.96 +AcmeSaaS Inc.,Marketing,2024,6,2024-06,Software & Tools,8183.04,7668.36,-514.68,-6.29 +AcmeSaaS Inc.,Marketing,2024,6,2024-06,Travel,11511.38,11024.51,-486.87,-4.23 +AcmeSaaS Inc.,Marketing,2024,6,2024-06,Marketing Spend,9410.66,8674.93,-735.73,-7.82 +AcmeSaaS Inc.,Marketing,2024,6,2024-06,Cloud Infrastructure,22831.95,20379.25,-2452.7,-10.74 +AcmeSaaS Inc.,Marketing,2024,6,2024-06,Contractors,5308.45,5616.41,307.96,5.8 +AcmeSaaS Inc.,Marketing,2024,6,2024-06,Office & Facilities,5007.24,5038.59,31.35,0.63 +AcmeSaaS Inc.,Operations,2024,6,2024-06,Salaries,7977.46,7103.19,-874.27,-10.96 +AcmeSaaS Inc.,Operations,2024,6,2024-06,Software & Tools,5621.65,6003.78,382.13,6.8 +AcmeSaaS Inc.,Operations,2024,6,2024-06,Travel,8260.35,8987.88,727.53,8.81 +AcmeSaaS Inc.,Operations,2024,6,2024-06,Marketing Spend,5945.11,5975.72,30.61,0.51 +AcmeSaaS Inc.,Operations,2024,6,2024-06,Cloud Infrastructure,2410.91,2386.63,-24.28,-1.01 +AcmeSaaS Inc.,Operations,2024,6,2024-06,Contractors,5862.92,6515.85,652.93,11.14 +AcmeSaaS Inc.,Operations,2024,6,2024-06,Office & Facilities,9724.09,8699.15,-1024.94,-10.54 +AcmeSaaS Inc.,Engineering,2024,7,2024-07,Salaries,17731.82,18192.69,460.87,2.6 +AcmeSaaS Inc.,Engineering,2024,7,2024-07,Software & Tools,15607.11,13980.28,-1626.83,-10.42 +AcmeSaaS Inc.,Engineering,2024,7,2024-07,Travel,23419.96,22155.37,-1264.59,-5.4 +AcmeSaaS Inc.,Engineering,2024,7,2024-07,Marketing Spend,18041.8,18618.03,576.23,3.19 +AcmeSaaS Inc.,Engineering,2024,7,2024-07,Cloud Infrastructure,29560.9,29903.97,343.07,1.16 +AcmeSaaS Inc.,Engineering,2024,7,2024-07,Contractors,6595.58,6318.86,-276.72,-4.2 +AcmeSaaS Inc.,Engineering,2024,7,2024-07,Office & Facilities,6796.06,7602.82,806.76,11.87 +AcmeSaaS Inc.,Sales,2024,7,2024-07,Salaries,13295.08,14153.29,858.21,6.46 +AcmeSaaS Inc.,Sales,2024,7,2024-07,Software & Tools,11829.82,12456.82,627.0,5.3 +AcmeSaaS Inc.,Sales,2024,7,2024-07,Travel,14722.74,13715.79,-1006.95,-6.84 +AcmeSaaS Inc.,Sales,2024,7,2024-07,Marketing Spend,5069.3,5010.36,-58.94,-1.16 +AcmeSaaS Inc.,Sales,2024,7,2024-07,Cloud Infrastructure,16560.05,15480.97,-1079.08,-6.52 +AcmeSaaS Inc.,Sales,2024,7,2024-07,Contractors,19439.67,18688.2,-751.47,-3.87 +AcmeSaaS Inc.,Sales,2024,7,2024-07,Office & Facilities,15590.17,15416.18,-173.99,-1.12 +AcmeSaaS Inc.,Marketing,2024,7,2024-07,Salaries,9928.85,8897.36,-1031.49,-10.39 +AcmeSaaS Inc.,Marketing,2024,7,2024-07,Software & Tools,4460.44,4815.6,355.16,7.96 +AcmeSaaS Inc.,Marketing,2024,7,2024-07,Travel,10112.45,9125.22,-987.23,-9.76 +AcmeSaaS Inc.,Marketing,2024,7,2024-07,Marketing Spend,14173.99,12801.6,-1372.39,-9.68 +AcmeSaaS Inc.,Marketing,2024,7,2024-07,Cloud Infrastructure,9218.45,9746.77,528.32,5.73 +AcmeSaaS Inc.,Marketing,2024,7,2024-07,Contractors,5441.18,5848.31,407.13,7.48 +AcmeSaaS Inc.,Marketing,2024,7,2024-07,Office & Facilities,18568.39,18819.6,251.21,1.35 +AcmeSaaS Inc.,Operations,2024,7,2024-07,Salaries,7665.93,8343.15,677.22,8.83 +AcmeSaaS Inc.,Operations,2024,7,2024-07,Software & Tools,7412.7,7805.98,393.28,5.31 +AcmeSaaS Inc.,Operations,2024,7,2024-07,Travel,5051.84,5619.75,567.91,11.24 +AcmeSaaS Inc.,Operations,2024,7,2024-07,Marketing Spend,2940.62,3011.48,70.86,2.41 +AcmeSaaS Inc.,Operations,2024,7,2024-07,Cloud Infrastructure,5295.64,5107.09,-188.55,-3.56 +AcmeSaaS Inc.,Operations,2024,7,2024-07,Contractors,8468.78,8627.15,158.37,1.87 +AcmeSaaS Inc.,Operations,2024,7,2024-07,Office & Facilities,9333.4,8689.93,-643.47,-6.89 +AcmeSaaS Inc.,Engineering,2024,8,2024-08,Salaries,20852.56,22390.08,1537.52,7.37 +AcmeSaaS Inc.,Engineering,2024,8,2024-08,Software & Tools,9899.78,10719.85,820.07,8.28 +AcmeSaaS Inc.,Engineering,2024,8,2024-08,Travel,6961.42,7754.26,792.84,11.39 +AcmeSaaS Inc.,Engineering,2024,8,2024-08,Marketing Spend,25629.77,27588.46,1958.69,7.64 +AcmeSaaS Inc.,Engineering,2024,8,2024-08,Cloud Infrastructure,13529.24,13898.01,368.77,2.73 +AcmeSaaS Inc.,Engineering,2024,8,2024-08,Contractors,23533.69,24339.67,805.98,3.42 +AcmeSaaS Inc.,Engineering,2024,8,2024-08,Office & Facilities,18759.81,16626.84,-2132.97,-11.37 +AcmeSaaS Inc.,Sales,2024,8,2024-08,Salaries,22785.27,20084.43,-2700.84,-11.85 +AcmeSaaS Inc.,Sales,2024,8,2024-08,Software & Tools,20713.68,22552.37,1838.69,8.88 +AcmeSaaS Inc.,Sales,2024,8,2024-08,Travel,9027.06,9170.74,143.68,1.59 +AcmeSaaS Inc.,Sales,2024,8,2024-08,Marketing Spend,7217.31,7045.45,-171.86,-2.38 +AcmeSaaS Inc.,Sales,2024,8,2024-08,Cloud Infrastructure,18077.76,16523.98,-1553.78,-8.59 +AcmeSaaS Inc.,Sales,2024,8,2024-08,Contractors,9890.79,10206.91,316.12,3.2 +AcmeSaaS Inc.,Sales,2024,8,2024-08,Office & Facilities,10532.08,9345.72,-1186.36,-11.26 +AcmeSaaS Inc.,Marketing,2024,8,2024-08,Salaries,14003.56,14230.73,227.17,1.62 +AcmeSaaS Inc.,Marketing,2024,8,2024-08,Software & Tools,5857.45,5273.99,-583.46,-9.96 +AcmeSaaS Inc.,Marketing,2024,8,2024-08,Travel,8997.88,8031.74,-966.14,-10.74 +AcmeSaaS Inc.,Marketing,2024,8,2024-08,Marketing Spend,7786.86,7146.61,-640.25,-8.22 +AcmeSaaS Inc.,Marketing,2024,8,2024-08,Cloud Infrastructure,8234.18,8467.05,232.87,2.83 +AcmeSaaS Inc.,Marketing,2024,8,2024-08,Contractors,13627.44,14196.42,568.98,4.18 +AcmeSaaS Inc.,Marketing,2024,8,2024-08,Office & Facilities,14474.91,13683.2,-791.71,-5.47 +AcmeSaaS Inc.,Operations,2024,8,2024-08,Salaries,8909.54,8446.06,-463.48,-5.2 +AcmeSaaS Inc.,Operations,2024,8,2024-08,Software & Tools,7014.13,7314.59,300.46,4.28 +AcmeSaaS Inc.,Operations,2024,8,2024-08,Travel,6545.13,6524.13,-21.0,-0.32 +AcmeSaaS Inc.,Operations,2024,8,2024-08,Marketing Spend,4729.29,4918.99,189.7,4.01 +AcmeSaaS Inc.,Operations,2024,8,2024-08,Cloud Infrastructure,9909.57,8828.44,-1081.13,-10.91 +AcmeSaaS Inc.,Operations,2024,8,2024-08,Contractors,3015.89,2940.08,-75.81,-2.51 +AcmeSaaS Inc.,Operations,2024,8,2024-08,Office & Facilities,6414.7,6567.61,152.91,2.38 +AcmeSaaS Inc.,Engineering,2024,9,2024-09,Salaries,8704.38,9220.41,516.03,5.93 +AcmeSaaS Inc.,Engineering,2024,9,2024-09,Software & Tools,23368.57,21549.72,-1818.85,-7.78 +AcmeSaaS Inc.,Engineering,2024,9,2024-09,Travel,18866.18,18323.77,-542.41,-2.88 +AcmeSaaS Inc.,Engineering,2024,9,2024-09,Marketing Spend,15171.87,15913.49,741.62,4.89 +AcmeSaaS Inc.,Engineering,2024,9,2024-09,Cloud Infrastructure,21077.08,21078.41,1.33,0.01 +AcmeSaaS Inc.,Engineering,2024,9,2024-09,Contractors,24701.68,26677.94,1976.26,8.0 +AcmeSaaS Inc.,Engineering,2024,9,2024-09,Office & Facilities,8706.51,9346.33,639.82,7.35 +AcmeSaaS Inc.,Sales,2024,9,2024-09,Salaries,5281.88,5374.93,93.05,1.76 +AcmeSaaS Inc.,Sales,2024,9,2024-09,Software & Tools,22752.78,23896.78,1144.0,5.03 +AcmeSaaS Inc.,Sales,2024,9,2024-09,Travel,4623.18,4531.86,-91.32,-1.98 +AcmeSaaS Inc.,Sales,2024,9,2024-09,Marketing Spend,4101.93,3723.08,-378.85,-9.24 +AcmeSaaS Inc.,Sales,2024,9,2024-09,Cloud Infrastructure,24066.89,21299.33,-2767.56,-11.5 +AcmeSaaS Inc.,Sales,2024,9,2024-09,Contractors,22760.42,21803.22,-957.2,-4.21 +AcmeSaaS Inc.,Sales,2024,9,2024-09,Office & Facilities,16425.26,17613.09,1187.83,7.23 +AcmeSaaS Inc.,Marketing,2024,9,2024-09,Salaries,10966.0,11029.07,63.07,0.58 +AcmeSaaS Inc.,Marketing,2024,9,2024-09,Software & Tools,13954.86,13605.77,-349.09,-2.5 +AcmeSaaS Inc.,Marketing,2024,9,2024-09,Travel,15180.92,14489.67,-691.25,-4.55 +AcmeSaaS Inc.,Marketing,2024,9,2024-09,Marketing Spend,3560.31,3423.18,-137.13,-3.85 +AcmeSaaS Inc.,Marketing,2024,9,2024-09,Cloud Infrastructure,14128.95,13562.89,-566.06,-4.01 +AcmeSaaS Inc.,Marketing,2024,9,2024-09,Contractors,5728.75,5272.47,-456.28,-7.96 +AcmeSaaS Inc.,Marketing,2024,9,2024-09,Office & Facilities,10557.23,10583.79,26.56,0.25 +AcmeSaaS Inc.,Operations,2024,9,2024-09,Salaries,2435.13,2281.0,-154.13,-6.33 +AcmeSaaS Inc.,Operations,2024,9,2024-09,Software & Tools,5869.94,5371.86,-498.08,-8.49 +AcmeSaaS Inc.,Operations,2024,9,2024-09,Travel,9305.14,8629.08,-676.06,-7.27 +AcmeSaaS Inc.,Operations,2024,9,2024-09,Marketing Spend,4476.87,4586.89,110.02,2.46 +AcmeSaaS Inc.,Operations,2024,9,2024-09,Cloud Infrastructure,7756.2,8240.59,484.39,6.25 +AcmeSaaS Inc.,Operations,2024,9,2024-09,Contractors,8550.61,8869.74,319.13,3.73 +AcmeSaaS Inc.,Operations,2024,9,2024-09,Office & Facilities,8516.67,7856.76,-659.91,-7.75 +AcmeSaaS Inc.,Engineering,2024,10,2024-10,Salaries,19482.03,20114.64,632.61,3.25 +AcmeSaaS Inc.,Engineering,2024,10,2024-10,Software & Tools,13702.19,14111.68,409.49,2.99 +AcmeSaaS Inc.,Engineering,2024,10,2024-10,Travel,19100.43,20770.18,1669.75,8.74 +AcmeSaaS Inc.,Engineering,2024,10,2024-10,Marketing Spend,19213.06,19799.68,586.62,3.05 +AcmeSaaS Inc.,Engineering,2024,10,2024-10,Cloud Infrastructure,12764.66,11695.36,-1069.3,-8.38 +AcmeSaaS Inc.,Engineering,2024,10,2024-10,Contractors,22619.55,20275.91,-2343.64,-10.36 +AcmeSaaS Inc.,Engineering,2024,10,2024-10,Office & Facilities,15161.5,14951.21,-210.29,-1.39 +AcmeSaaS Inc.,Sales,2024,10,2024-10,Salaries,15286.7,16503.63,1216.93,7.96 +AcmeSaaS Inc.,Sales,2024,10,2024-10,Software & Tools,14370.23,12910.44,-1459.79,-10.16 +AcmeSaaS Inc.,Sales,2024,10,2024-10,Travel,7255.72,7890.02,634.3,8.74 +AcmeSaaS Inc.,Sales,2024,10,2024-10,Marketing Spend,21945.84,23817.17,1871.33,8.53 +AcmeSaaS Inc.,Sales,2024,10,2024-10,Cloud Infrastructure,15533.74,15962.5,428.76,2.76 +AcmeSaaS Inc.,Sales,2024,10,2024-10,Contractors,20141.24,20175.41,34.17,0.17 +AcmeSaaS Inc.,Sales,2024,10,2024-10,Office & Facilities,7279.1,7213.96,-65.14,-0.89 +AcmeSaaS Inc.,Marketing,2024,10,2024-10,Salaries,9608.88,9552.68,-56.2,-0.58 +AcmeSaaS Inc.,Marketing,2024,10,2024-10,Software & Tools,12774.18,11703.79,-1070.39,-8.38 +AcmeSaaS Inc.,Marketing,2024,10,2024-10,Travel,14161.02,12671.98,-1489.04,-10.52 +AcmeSaaS Inc.,Marketing,2024,10,2024-10,Marketing Spend,8215.06,7433.32,-781.74,-9.52 +AcmeSaaS Inc.,Marketing,2024,10,2024-10,Cloud Infrastructure,13014.05,14260.67,1246.62,9.58 +AcmeSaaS Inc.,Marketing,2024,10,2024-10,Contractors,10908.59,10498.7,-409.89,-3.76 +AcmeSaaS Inc.,Marketing,2024,10,2024-10,Office & Facilities,6506.39,6841.05,334.66,5.14 +AcmeSaaS Inc.,Operations,2024,10,2024-10,Salaries,8695.16,8429.82,-265.34,-3.05 +AcmeSaaS Inc.,Operations,2024,10,2024-10,Software & Tools,4394.44,4165.47,-228.97,-5.21 +AcmeSaaS Inc.,Operations,2024,10,2024-10,Travel,5368.41,5250.87,-117.54,-2.19 +AcmeSaaS Inc.,Operations,2024,10,2024-10,Marketing Spend,7829.93,7526.19,-303.74,-3.88 +AcmeSaaS Inc.,Operations,2024,10,2024-10,Cloud Infrastructure,7851.48,8035.93,184.45,2.35 +AcmeSaaS Inc.,Operations,2024,10,2024-10,Contractors,8930.91,9550.84,619.93,6.94 +AcmeSaaS Inc.,Operations,2024,10,2024-10,Office & Facilities,4215.51,4364.54,149.03,3.54 +AcmeSaaS Inc.,Engineering,2024,11,2024-11,Salaries,6277.04,6839.38,562.34,8.96 +AcmeSaaS Inc.,Engineering,2024,11,2024-11,Software & Tools,7048.76,6766.86,-281.9,-4.0 +AcmeSaaS Inc.,Engineering,2024,11,2024-11,Travel,22806.85,23259.74,452.89,1.99 +AcmeSaaS Inc.,Engineering,2024,11,2024-11,Marketing Spend,12166.96,11119.91,-1047.05,-8.61 +AcmeSaaS Inc.,Engineering,2024,11,2024-11,Cloud Infrastructure,24030.92,23164.77,-866.15,-3.6 +AcmeSaaS Inc.,Engineering,2024,11,2024-11,Contractors,22218.08,24712.0,2493.92,11.22 +AcmeSaaS Inc.,Engineering,2024,11,2024-11,Office & Facilities,28959.33,30338.81,1379.48,4.76 +AcmeSaaS Inc.,Sales,2024,11,2024-11,Salaries,10756.3,11195.46,439.16,4.08 +AcmeSaaS Inc.,Sales,2024,11,2024-11,Software & Tools,14666.66,15824.6,1157.94,7.9 +AcmeSaaS Inc.,Sales,2024,11,2024-11,Travel,21270.37,22489.29,1218.92,5.73 +AcmeSaaS Inc.,Sales,2024,11,2024-11,Marketing Spend,9170.15,9578.22,408.07,4.45 +AcmeSaaS Inc.,Sales,2024,11,2024-11,Cloud Infrastructure,10462.11,10528.38,66.27,0.63 +AcmeSaaS Inc.,Sales,2024,11,2024-11,Contractors,18452.59,19099.28,646.69,3.5 +AcmeSaaS Inc.,Sales,2024,11,2024-11,Office & Facilities,18867.01,18520.19,-346.82,-1.84 +AcmeSaaS Inc.,Marketing,2024,11,2024-11,Salaries,10221.06,9331.99,-889.07,-8.7 +AcmeSaaS Inc.,Marketing,2024,11,2024-11,Software & Tools,10235.94,9197.19,-1038.75,-10.15 +AcmeSaaS Inc.,Marketing,2024,11,2024-11,Travel,6709.6,7264.23,554.63,8.27 +AcmeSaaS Inc.,Marketing,2024,11,2024-11,Marketing Spend,7365.8,6660.7,-705.1,-9.57 +AcmeSaaS Inc.,Marketing,2024,11,2024-11,Cloud Infrastructure,21551.18,22952.22,1401.04,6.5 +AcmeSaaS Inc.,Marketing,2024,11,2024-11,Contractors,12627.78,13643.42,1015.64,8.04 +AcmeSaaS Inc.,Marketing,2024,11,2024-11,Office & Facilities,7604.65,8304.91,700.26,9.21 +AcmeSaaS Inc.,Operations,2024,11,2024-11,Salaries,2558.05,2724.49,166.44,6.51 +AcmeSaaS Inc.,Operations,2024,11,2024-11,Software & Tools,6299.96,6767.23,467.27,7.42 +AcmeSaaS Inc.,Operations,2024,11,2024-11,Travel,11675.29,10738.11,-937.18,-8.03 +AcmeSaaS Inc.,Operations,2024,11,2024-11,Marketing Spend,3725.63,3669.9,-55.73,-1.5 +AcmeSaaS Inc.,Operations,2024,11,2024-11,Cloud Infrastructure,6799.97,6654.49,-145.48,-2.14 +AcmeSaaS Inc.,Operations,2024,11,2024-11,Contractors,4116.05,4290.27,174.22,4.23 +AcmeSaaS Inc.,Operations,2024,11,2024-11,Office & Facilities,12489.18,11702.45,-786.73,-6.3 +AcmeSaaS Inc.,Engineering,2024,12,2024-12,Salaries,16089.84,15970.19,-119.65,-0.74 +AcmeSaaS Inc.,Engineering,2024,12,2024-12,Software & Tools,11894.74,12851.4,956.66,8.04 +AcmeSaaS Inc.,Engineering,2024,12,2024-12,Travel,24105.92,23341.32,-764.6,-3.17 +AcmeSaaS Inc.,Engineering,2024,12,2024-12,Marketing Spend,16214.4,17954.39,1739.99,10.73 +AcmeSaaS Inc.,Engineering,2024,12,2024-12,Cloud Infrastructure,18455.45,20601.18,2145.73,11.63 +AcmeSaaS Inc.,Engineering,2024,12,2024-12,Contractors,12541.11,12425.77,-115.34,-0.92 +AcmeSaaS Inc.,Engineering,2024,12,2024-12,Office & Facilities,25688.58,24343.15,-1345.43,-5.24 +AcmeSaaS Inc.,Sales,2024,12,2024-12,Salaries,11463.43,11851.85,388.42,3.39 +AcmeSaaS Inc.,Sales,2024,12,2024-12,Software & Tools,14505.93,15808.39,1302.46,8.98 +AcmeSaaS Inc.,Sales,2024,12,2024-12,Travel,23676.2,23986.12,309.92,1.31 +AcmeSaaS Inc.,Sales,2024,12,2024-12,Marketing Spend,20554.51,18594.05,-1960.46,-9.54 +AcmeSaaS Inc.,Sales,2024,12,2024-12,Cloud Infrastructure,20227.83,21907.03,1679.2,8.3 +AcmeSaaS Inc.,Sales,2024,12,2024-12,Contractors,6375.29,6912.6,537.31,8.43 +AcmeSaaS Inc.,Sales,2024,12,2024-12,Office & Facilities,8707.61,8258.43,-449.18,-5.16 +AcmeSaaS Inc.,Marketing,2024,12,2024-12,Salaries,14815.77,14641.99,-173.78,-1.17 +AcmeSaaS Inc.,Marketing,2024,12,2024-12,Software & Tools,7002.61,6749.82,-252.79,-3.61 +AcmeSaaS Inc.,Marketing,2024,12,2024-12,Travel,17081.5,15141.06,-1940.44,-11.36 +AcmeSaaS Inc.,Marketing,2024,12,2024-12,Marketing Spend,5003.72,4467.23,-536.49,-10.72 +AcmeSaaS Inc.,Marketing,2024,12,2024-12,Cloud Infrastructure,9626.74,9631.38,4.64,0.05 +AcmeSaaS Inc.,Marketing,2024,12,2024-12,Contractors,17736.53,16611.8,-1124.73,-6.34 +AcmeSaaS Inc.,Marketing,2024,12,2024-12,Office & Facilities,6193.87,6929.0,735.13,11.87 +AcmeSaaS Inc.,Operations,2024,12,2024-12,Salaries,5290.09,5019.51,-270.58,-5.11 +AcmeSaaS Inc.,Operations,2024,12,2024-12,Software & Tools,1903.32,2053.95,150.63,7.91 +AcmeSaaS Inc.,Operations,2024,12,2024-12,Travel,10720.19,11224.65,504.46,4.71 +AcmeSaaS Inc.,Operations,2024,12,2024-12,Marketing Spend,9824.97,8973.25,-851.72,-8.67 +AcmeSaaS Inc.,Operations,2024,12,2024-12,Cloud Infrastructure,7976.73,8370.21,393.48,4.93 +AcmeSaaS Inc.,Operations,2024,12,2024-12,Contractors,9358.11,9242.67,-115.44,-1.23 +AcmeSaaS Inc.,Operations,2024,12,2024-12,Office & Facilities,2972.04,2619.14,-352.9,-11.87 diff --git a/testing/data/csv/pl_income_statement.csv b/testing/data/csv/pl_income_statement.csv new file mode 100644 index 0000000..0076b2c --- /dev/null +++ b/testing/data/csv/pl_income_statement.csv @@ -0,0 +1,25 @@ +company,year,month,period,product_revenue,service_revenue,total_revenue,cogs_product,cogs_service,total_cogs,gross_profit,gross_margin_pct,total_opex,ebitda,ebitda_margin_pct,net_income +AcmeSaaS Inc.,2023,1,2023-01,167881.7,71338.86,239220.56,43649.24,32102.49,75751.73,163468.83,68.33,258734.13,-95265.3,-39.82,-71448.98 +AcmeSaaS Inc.,2023,2,2023-02,178629.98,69239.49,247869.47,44657.5,32542.56,77200.06,170669.41,68.85,262477.89,-91808.48,-37.04,-68856.36 +AcmeSaaS Inc.,2023,3,2023-03,174480.8,77504.48,251985.28,41875.39,33326.93,75202.32,176782.96,70.16,263811.87,-87028.91,-34.54,-65271.68 +AcmeSaaS Inc.,2023,4,2023-04,198938.2,71926.16,270864.36,51723.93,30928.25,82652.18,188212.18,69.49,277471.78,-89259.6,-32.95,-66944.7 +AcmeSaaS Inc.,2023,5,2023-05,195203.87,79126.8,274330.67,48800.97,37980.86,86781.83,187548.84,68.37,264394.63,-76845.79,-28.01,-57634.34 +AcmeSaaS Inc.,2023,6,2023-06,211243.76,82868.84,294112.6,54923.38,38119.67,93043.05,201069.55,68.36,271048.89,-69979.34,-23.79,-52484.5 +AcmeSaaS Inc.,2023,7,2023-07,197893.06,86586.64,284479.7,49473.26,39829.85,89303.11,195176.59,68.61,286660.29,-91483.7,-32.16,-68612.77 +AcmeSaaS Inc.,2023,8,2023-08,212514.63,79799.67,292314.3,51003.51,34313.86,85317.37,206996.93,70.81,287657.57,-80660.64,-27.59,-60495.48 +AcmeSaaS Inc.,2023,9,2023-09,220822.47,80318.59,301141.06,57413.84,34536.99,91950.83,209190.23,69.47,294485.72,-85295.49,-28.32,-63971.62 +AcmeSaaS Inc.,2023,10,2023-10,211288.73,83490.66,294779.39,54935.07,38405.7,93340.77,201438.62,68.34,306228.63,-104790.01,-35.55,-78592.51 +AcmeSaaS Inc.,2023,11,2023-11,230323.91,94427.01,324750.92,57580.98,44380.69,101961.67,222789.25,68.6,299769.68,-76980.43,-23.7,-57735.32 +AcmeSaaS Inc.,2023,12,2023-12,237893.99,85187.11,323081.1,57094.56,39186.07,96280.63,226800.47,70.2,300204.79,-73404.32,-22.72,-55053.24 +AcmeSaaS Inc.,2024,1,2024-01,236329.99,81463.84,317793.83,61445.8,35844.09,97289.89,220503.94,69.39,323370.74,-102866.8,-32.37,-77150.1 +AcmeSaaS Inc.,2024,2,2024-02,231260.65,93519.61,324780.26,57815.16,43954.22,101769.38,223010.88,68.67,318477.89,-95467.01,-29.39,-71600.26 +AcmeSaaS Inc.,2024,3,2024-03,265782.9,83786.47,349569.37,63787.9,39379.64,103167.54,246401.83,70.49,311338.28,-64936.45,-18.58,-48702.34 +AcmeSaaS Inc.,2024,4,2024-04,268162.53,102184.41,370346.94,67040.63,47004.83,114045.46,256301.48,69.21,321097.34,-64795.86,-17.5,-48596.9 +AcmeSaaS Inc.,2024,5,2024-05,256505.97,87815.99,344321.96,66691.55,39517.2,106208.75,238113.21,69.15,316380.7,-78267.49,-22.73,-58700.62 +AcmeSaaS Inc.,2024,6,2024-06,271536.83,106191.45,377728.28,65168.84,49909.98,115078.82,262649.46,69.53,325000.96,-62351.5,-16.51,-46763.62 +AcmeSaaS Inc.,2024,7,2024-07,298646.5,96974.17,395620.67,71675.16,44608.12,116283.28,279337.39,70.61,330776.36,-51438.97,-13.0,-38579.23 +AcmeSaaS Inc.,2024,8,2024-08,289698.65,107310.47,397009.12,75321.65,49362.82,124684.47,272324.65,68.59,354189.36,-81864.71,-20.62,-61398.53 +AcmeSaaS Inc.,2024,9,2024-09,277082.24,94973.08,372055.32,66499.74,44637.35,111137.09,260918.23,70.13,332157.93,-71239.7,-19.15,-53429.77 +AcmeSaaS Inc.,2024,10,2024-10,304823.0,104824.94,409647.94,73157.52,46122.97,119280.49,290367.45,70.88,365913.14,-75545.69,-18.44,-56659.27 +AcmeSaaS Inc.,2024,11,2024-11,325914.43,95195.56,421109.99,84737.75,43789.96,128527.71,292582.28,69.48,354860.87,-62278.59,-14.79,-46708.94 +AcmeSaaS Inc.,2024,12,2024-12,331873.11,108581.53,440454.64,79649.55,52119.13,131768.68,308685.96,70.08,352490.46,-43804.5,-9.95,-32853.38 diff --git a/testing/data/csv/revenue_budget_vs_actuals.csv b/testing/data/csv/revenue_budget_vs_actuals.csv new file mode 100644 index 0000000..8c99619 --- /dev/null +++ b/testing/data/csv/revenue_budget_vs_actuals.csv @@ -0,0 +1,49 @@ +company,year,month,period,revenue_type,budget_amount,actual_amount,variance,variance_pct +AcmeSaaS Inc.,2023,1,2023-01,Product,180000,185019.36,5019.36,2.79 +AcmeSaaS Inc.,2023,1,2023-01,Service,75000,67875.16,-7124.84,-9.5 +AcmeSaaS Inc.,2023,2,2023-02,Product,184500.0,176198.58,-8301.42,-4.5 +AcmeSaaS Inc.,2023,2,2023-02,Service,76125.0,71910.88,-4214.12,-5.54 +AcmeSaaS Inc.,2023,3,2023-03,Product,189112.5,198056.43,8943.93,4.73 +AcmeSaaS Inc.,2023,3,2023-03,Service,77266.87,79997.47,2730.6,3.53 +AcmeSaaS Inc.,2023,4,2023-04,Product,193840.31,209044.35,15204.04,7.84 +AcmeSaaS Inc.,2023,4,2023-04,Service,78425.88,71946.94,-6478.94,-8.26 +AcmeSaaS Inc.,2023,5,2023-05,Product,198686.32,195583.71,-3102.61,-1.56 +AcmeSaaS Inc.,2023,5,2023-05,Service,79602.27,72116.43,-7485.84,-9.4 +AcmeSaaS Inc.,2023,6,2023-06,Product,203653.48,192193.41,-11460.07,-5.63 +AcmeSaaS Inc.,2023,6,2023-06,Service,80796.3,80882.84,86.54,0.11 +AcmeSaaS Inc.,2023,7,2023-07,Product,208744.82,188978.19,-19766.63,-9.47 +AcmeSaaS Inc.,2023,7,2023-07,Service,82008.24,77068.68,-4939.56,-6.02 +AcmeSaaS Inc.,2023,8,2023-08,Product,213963.44,220377.4,6413.96,3.0 +AcmeSaaS Inc.,2023,8,2023-08,Service,83238.37,83986.54,748.17,0.9 +AcmeSaaS Inc.,2023,9,2023-09,Product,219312.52,207050.35,-12262.17,-5.59 +AcmeSaaS Inc.,2023,9,2023-09,Service,84486.94,85995.3,1508.36,1.79 +AcmeSaaS Inc.,2023,10,2023-10,Product,224795.33,238707.03,13911.7,6.19 +AcmeSaaS Inc.,2023,10,2023-10,Service,85754.25,77290.28,-8463.97,-9.87 +AcmeSaaS Inc.,2023,11,2023-11,Product,230415.22,244508.3,14093.08,6.12 +AcmeSaaS Inc.,2023,11,2023-11,Service,87040.56,90489.79,3449.23,3.96 +AcmeSaaS Inc.,2023,12,2023-12,Product,236175.6,228629.81,-7545.79,-3.19 +AcmeSaaS Inc.,2023,12,2023-12,Service,88346.17,82258.76,-6087.41,-6.89 +AcmeSaaS Inc.,2024,1,2024-01,Product,242079.99,264216.42,22136.43,9.14 +AcmeSaaS Inc.,2024,1,2024-01,Service,89671.36,86740.8,-2930.56,-3.27 +AcmeSaaS Inc.,2024,2,2024-02,Product,248131.99,227921.43,-20210.56,-8.15 +AcmeSaaS Inc.,2024,2,2024-02,Service,91016.43,83675.34,-7341.09,-8.07 +AcmeSaaS Inc.,2024,3,2024-03,Product,254335.29,272011.31,17676.02,6.95 +AcmeSaaS Inc.,2024,3,2024-03,Service,92381.68,94298.16,1916.48,2.07 +AcmeSaaS Inc.,2024,4,2024-04,Product,260693.67,276706.95,16013.28,6.14 +AcmeSaaS Inc.,2024,4,2024-04,Service,93767.4,98075.67,4308.27,4.59 +AcmeSaaS Inc.,2024,5,2024-05,Product,267211.01,269147.12,1936.11,0.72 +AcmeSaaS Inc.,2024,5,2024-05,Service,95173.92,104179.58,9005.66,9.46 +AcmeSaaS Inc.,2024,6,2024-06,Product,273891.29,267237.61,-6653.68,-2.43 +AcmeSaaS Inc.,2024,6,2024-06,Service,96601.52,97606.96,1005.44,1.04 +AcmeSaaS Inc.,2024,7,2024-07,Product,280738.57,299233.89,18495.32,6.59 +AcmeSaaS Inc.,2024,7,2024-07,Service,98050.55,100374.74,2324.19,2.37 +AcmeSaaS Inc.,2024,8,2024-08,Product,287757.03,308573.77,20816.74,7.23 +AcmeSaaS Inc.,2024,8,2024-08,Service,99521.31,101060.95,1539.64,1.55 +AcmeSaaS Inc.,2024,9,2024-09,Product,294950.96,307018.69,12067.73,4.09 +AcmeSaaS Inc.,2024,9,2024-09,Service,101014.13,91838.5,-9175.63,-9.08 +AcmeSaaS Inc.,2024,10,2024-10,Product,302324.73,285872.11,-16452.62,-5.44 +AcmeSaaS Inc.,2024,10,2024-10,Service,102529.34,98210.56,-4318.78,-4.21 +AcmeSaaS Inc.,2024,11,2024-11,Product,309882.85,283839.8,-26043.05,-8.4 +AcmeSaaS Inc.,2024,11,2024-11,Service,104067.28,98505.73,-5561.55,-5.34 +AcmeSaaS Inc.,2024,12,2024-12,Product,317629.92,292283.14,-25346.78,-7.98 +AcmeSaaS Inc.,2024,12,2024-12,Service,105628.29,100937.84,-4690.45,-4.44 diff --git a/testing/generators/generate_data.py b/testing/generators/generate_data.py new file mode 100644 index 0000000..0bdf7a9 --- /dev/null +++ b/testing/generators/generate_data.py @@ -0,0 +1,382 @@ +""" +FP&A Test Data Generator +Generates realistic CSV data for: Budget vs Actuals, Cash Flow, P&L, Headcount +Covers 2 years (2023–2024), 4 departments, product & service revenue mix. +""" + +import csv +import random +import os +from datetime import date, timedelta +from dataclasses import dataclass, fields, asdict +from typing import List + +random.seed(42) # reproducible data + +OUTPUT_DIR = os.path.join(os.path.dirname(__file__), "..", "data", "csv") +os.makedirs(OUTPUT_DIR, exist_ok=True) + +# ── Company config ──────────────────────────────────────────────────────────── +COMPANY = "AcmeSaaS Inc." +DEPARTMENTS = ["Engineering", "Sales", "Marketing", "Operations"] +YEARS = [2023, 2024] + +# Revenue split: product (SaaS subscriptions) vs service (consulting/support) +PRODUCT_REVENUE_MIX = 0.70 # 70% product (recurring SaaS) +SERVICE_REVENUE_MIX = 0.30 # 30% services + +# Monthly growth rates (realistic SaaS-style) +PRODUCT_MONTHLY_GROWTH = 0.025 # 2.5% MoM +SERVICE_MONTHLY_GROWTH = 0.015 # 1.5% MoM + +# Base monthly revenue ($) +BASE_PRODUCT_REVENUE = 180_000 +BASE_SERVICE_REVENUE = 75_000 + +# Variance helpers — actuals deviate from budget by ±% +def vary(value: float, pct: float = 0.08) -> float: + """Apply random variance to simulate actuals vs budget.""" + return round(value * (1 + random.uniform(-pct, pct)), 2) + +def months_range(years: List[int]): + for year in years: + for month in range(1, 13): + yield year, month + +# ── 1. Revenue (Budget vs Actuals) ─────────────────────────────────────────── +@dataclass +class RevenueRow: + company: str + year: int + month: int + period: str # e.g. "2023-01" + revenue_type: str # "Product" | "Service" + budget_amount: float + actual_amount: float + variance: float # actual - budget + variance_pct: float # variance / budget * 100 + +def generate_revenue(): + rows = [] + prod_base = BASE_PRODUCT_REVENUE + svc_base = BASE_SERVICE_REVENUE + + for year, month in months_range(YEARS): + period = f"{year}-{month:02d}" + + for rev_type, base in [("Product", prod_base), ("Service", svc_base)]: + budget = round(base, 2) + actual = vary(budget, pct=0.10) + variance = round(actual - budget, 2) + vpct = round((variance / budget) * 100, 2) if budget else 0 + rows.append(RevenueRow(COMPANY, year, month, period, rev_type, + budget, actual, variance, vpct)) + + # grow base each month + prod_base *= (1 + PRODUCT_MONTHLY_GROWTH) + svc_base *= (1 + SERVICE_MONTHLY_GROWTH) + + write_csv("revenue_budget_vs_actuals.csv", rows) + print(f" ✓ revenue_budget_vs_actuals.csv ({len(rows)} rows)") + +# ── 2. Department Opex (Budget vs Actuals) ─────────────────────────────────── +DEPT_BUDGETS = { + # (base_monthly_opex, growth_rate) + "Engineering": (95_000, 0.012), + "Sales": (70_000, 0.018), + "Marketing": (55_000, 0.015), + "Operations": (40_000, 0.008), +} + +OPEX_CATEGORIES = ["Salaries", "Software & Tools", "Travel", "Marketing Spend", + "Cloud Infrastructure", "Contractors", "Office & Facilities"] + +@dataclass +class OpexRow: + company: str + department: str + year: int + month: int + period: str + category: str + budget_amount: float + actual_amount: float + variance: float + variance_pct: float + +def generate_opex(): + rows = [] + dept_bases = {d: v[0] for d, v in DEPT_BUDGETS.items()} + + for year, month in months_range(YEARS): + period = f"{year}-{month:02d}" + for dept, (_, growth) in DEPT_BUDGETS.items(): + total_budget = dept_bases[dept] + # Split across categories with random weights + weights = [random.uniform(0.05, 0.35) for _ in OPEX_CATEGORIES] + total_w = sum(weights) + weights = [w / total_w for w in weights] + + for cat, w in zip(OPEX_CATEGORIES, weights): + budget = round(total_budget * w, 2) + actual = vary(budget, pct=0.12) + variance = round(actual - budget, 2) + vpct = round((variance / budget) * 100, 2) if budget else 0 + rows.append(OpexRow(COMPANY, dept, year, month, period, cat, + budget, actual, variance, vpct)) + + dept_bases[dept] *= (1 + growth) + + write_csv("opex_budget_vs_actuals.csv", rows) + print(f" ✓ opex_budget_vs_actuals.csv ({len(rows)} rows)") + +# ── 3. P&L / Income Statement ───────────────────────────────────────────────── +@dataclass +class PLRow: + company: str + year: int + month: int + period: str + product_revenue: float + service_revenue: float + total_revenue: float + cogs_product: float # ~25% of product rev + cogs_service: float # ~45% of service rev (labor-heavy) + total_cogs: float + gross_profit: float + gross_margin_pct: float + total_opex: float + ebitda: float + ebitda_margin_pct: float + net_income: float # after ~25% tax estimate + +def generate_pl(): + rows = [] + prod_base = BASE_PRODUCT_REVENUE + svc_base = BASE_SERVICE_REVENUE + dept_bases = {d: v[0] for d, v in DEPT_BUDGETS.items()} + + for year, month in months_range(YEARS): + period = f"{year}-{month:02d}" + + prod_rev = vary(prod_base, 0.08) + svc_rev = vary(svc_base, 0.10) + total_rev = round(prod_rev + svc_rev, 2) + + cogs_prod = round(prod_rev * vary(0.25, 0.05), 2) + cogs_svc = round(svc_rev * vary(0.45, 0.06), 2) + total_cogs = round(cogs_prod + cogs_svc, 2) + + gross_profit = round(total_rev - total_cogs, 2) + gm_pct = round((gross_profit / total_rev) * 100, 2) if total_rev else 0 + + total_opex = round(sum( + vary(dept_bases[d], 0.10) for d in DEPARTMENTS + ), 2) + + ebitda = round(gross_profit - total_opex, 2) + ebitda_pct = round((ebitda / total_rev) * 100, 2) if total_rev else 0 + net_income = round(ebitda * 0.75, 2) # rough 25% tax + + rows.append(PLRow( + COMPANY, year, month, period, + round(prod_rev, 2), round(svc_rev, 2), total_rev, + cogs_prod, cogs_svc, total_cogs, + gross_profit, gm_pct, + total_opex, ebitda, ebitda_pct, net_income + )) + + prod_base *= (1 + PRODUCT_MONTHLY_GROWTH) + svc_base *= (1 + SERVICE_MONTHLY_GROWTH) + for d, (_, g) in DEPT_BUDGETS.items(): + dept_bases[d] *= (1 + g) + + write_csv("pl_income_statement.csv", rows) + print(f" ✓ pl_income_statement.csv ({len(rows)} rows)") + +# ── 4. Cash Flow ────────────────────────────────────────────────────────────── +@dataclass +class CashFlowRow: + company: str + year: int + month: int + period: str + # Operating + cash_collected_product: float # product ARR collections (may lag revenue) + cash_collected_service: float + cash_paid_opex: float + cash_paid_cogs: float + net_operating_cash_flow: float + # Investing + capex: float # infra / hardware + net_investing_cash_flow: float + # Financing + loan_repayment: float + equity_raised: float + net_financing_cash_flow: float + # Summary + net_change_in_cash: float + closing_cash_balance: float + +def generate_cashflow(): + rows = [] + cash_balance = 1_200_000.0 # starting cash (seed round runway) + prod_base = BASE_PRODUCT_REVENUE + svc_base = BASE_SERVICE_REVENUE + dept_bases = {d: v[0] for d, v in DEPT_BUDGETS.items()} + + for year, month in months_range(YEARS): + period = f"{year}-{month:02d}" + + # Collections slightly lag invoicing (DSO ~30 days effect) + cash_prod = vary(prod_base * 0.95, 0.06) + cash_svc = vary(svc_base * 0.90, 0.08) # services collect slower + + opex_paid = sum(vary(dept_bases[d], 0.08) for d in DEPARTMENTS) + cogs_paid = vary((prod_base * 0.25) + (svc_base * 0.45), 0.07) + + net_op = round(cash_prod + cash_svc - opex_paid - cogs_paid, 2) + + # Investing — occasional capex spikes + capex = vary(8_000, 0.40) if random.random() > 0.4 else 0.0 + net_inv = round(-capex, 2) + + # Financing — occasional loan repayment + loan = vary(5_000, 0.20) if month % 3 == 0 else 0.0 + equity = 0.0 + if year == 2023 and month == 6: + equity = 500_000.0 # Series A mid-2023 + net_fin = round(equity - loan, 2) + + net_change = round(net_op + net_inv + net_fin, 2) + cash_balance = round(cash_balance + net_change, 2) + + rows.append(CashFlowRow( + COMPANY, year, month, period, + round(cash_prod, 2), round(cash_svc, 2), + round(opex_paid, 2), round(cogs_paid, 2), net_op, + round(capex, 2), net_inv, + round(loan, 2), round(equity, 2), net_fin, + net_change, cash_balance + )) + + prod_base *= (1 + PRODUCT_MONTHLY_GROWTH) + svc_base *= (1 + SERVICE_MONTHLY_GROWTH) + for d, (_, g) in DEPT_BUDGETS.items(): + dept_bases[d] *= (1 + g) + + write_csv("cash_flow.csv", rows) + print(f" ✓ cash_flow.csv ({len(rows)} rows)") + +# ── 5. Headcount & Workforce ────────────────────────────────────────────────── +ROLES = { + "Engineering": [("Software Engineer", 120_000), ("Senior Engineer", 160_000), + ("Engineering Manager", 180_000), ("DevOps Engineer", 130_000)], + "Sales": [("Account Executive", 90_000), ("Sales Manager", 140_000), + ("SDR", 65_000)], + "Marketing": [("Marketing Manager", 110_000), ("Content Strategist", 80_000), + ("Growth Analyst", 95_000)], + "Operations": [("Operations Manager", 115_000), ("Customer Success", 75_000), + ("Finance Analyst", 95_000)], +} + +@dataclass +class HeadcountRow: + company: str + employee_id: str + department: str + role: str + hire_date: str + termination_date: str # empty if active + status: str # Active | Terminated + annual_salary_budget: float + actual_salary_paid_ytd: float # YTD for the given year + year: int + month: int + period: str + headcount_fte: float # 1.0 full time, 0.5 contractor etc. + +def generate_headcount(): + rows = [] + emp_id = 1000 + employees = [] + + # Seed initial employees at start of 2023 + for dept, role_list in ROLES.items(): + # Start with 2-4 per department + count = random.randint(2, 4) + for _ in range(count): + role, salary = random.choice(role_list) + hire_date = date(2022, random.randint(1, 12), random.randint(1, 28)) + employees.append({ + "id": f"EMP{emp_id}", + "dept": dept, "role": role, "salary": salary, + "hire_date": hire_date, "term_date": None, + "fte": 1.0, + }) + emp_id += 1 + + for year, month in months_range(YEARS): + period = f"{year}-{month:02d}" + current = date(year, month, 1) + + # Random hiring each month + if random.random() > 0.55: + dept = random.choice(DEPARTMENTS) + role, salary = random.choice(ROLES[dept]) + employees.append({ + "id": f"EMP{emp_id}", + "dept": dept, "role": role, "salary": salary, + "hire_date": current, "term_date": None, + "fte": random.choice([1.0, 1.0, 1.0, 0.5]), # mostly FT + }) + emp_id += 1 + + # Occasional attrition + active = [e for e in employees if e["term_date"] is None] + if len(active) > 6 and random.random() > 0.85: + leaver = random.choice(active) + leaver["term_date"] = current + + # Snapshot each employee for this month + for emp in employees: + if emp["hire_date"] > current: + continue # not hired yet + status = "Active" if emp["term_date"] is None or emp["term_date"] > current else "Terminated" + months_in_year = month if emp["hire_date"].year < year else ( + month - emp["hire_date"].month + 1 + ) + months_in_year = max(0, min(months_in_year, month)) + ytd_paid = round((emp["salary"] / 12) * months_in_year * vary(1.0, 0.02), 2) + rows.append(HeadcountRow( + COMPANY, emp["id"], emp["dept"], emp["role"], + str(emp["hire_date"]), + str(emp["term_date"]) if emp["term_date"] else "", + status, emp["salary"], ytd_paid, + year, month, period, emp["fte"] + )) + + write_csv("headcount_workforce.csv", rows) + print(f" ✓ headcount_workforce.csv ({len(rows)} rows)") + +# ── CSV writer ──────────────────────────────────────────────────────────────── +def write_csv(filename: str, rows: list): + if not rows: + return + path = os.path.join(OUTPUT_DIR, filename) + with open(path, "w", newline="") as f: + writer = csv.DictWriter(f, fieldnames=[field.name for field in fields(rows[0])]) + writer.writeheader() + writer.writerows([asdict(r) for r in rows]) + +# ── Entry point ─────────────────────────────────────────────────────────────── +if __name__ == "__main__": + print(f"\n🏗 Generating FP&A test data for {COMPANY}") + print(f" Periods : {YEARS[0]}-01 → {YEARS[-1]}-12 (24 months)") + print(f" Depts : {', '.join(DEPARTMENTS)}\n") + generate_revenue() + generate_opex() + generate_pl() + generate_cashflow() + generate_headcount() + print(f"\n✅ All CSV files written to: {os.path.abspath(OUTPUT_DIR)}\n") diff --git a/testing/loader/api_loader.py b/testing/loader/api_loader.py new file mode 100644 index 0000000..0801826 --- /dev/null +++ b/testing/loader/api_loader.py @@ -0,0 +1,428 @@ +""" +FP&A API Loader +Reads generated CSV files and loads them into the Go FP&A API. + +Real endpoint map (from main.go): + POST /api/v1/budgets ← one budget record per request + PUT /api/v1/budgets/{id} ← update (not used in seeding) + DELETE /api/v1/budgets/{id} ← delete (not used in seeding) + POST /api/v1/actuals/ingest ← bulk actuals ingest + GET /api/v1/variance ← read-only, not loaded + GET /api/v1/variance/alerts ← read-only, not loaded + GET /api/v1/health ← health check + +Load order matters: + 1. Budgets first — actuals reference budget lines by category+period + 2. Actuals second — ingested in bulk against existing budgets +""" + +import csv +import json +import time +import os +import requests +from typing import List, Dict, Any, Optional, Callable +from dataclasses import dataclass + +DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data", "csv") + +# ── Config ──────────────────────────────────────────────────────────────────── +@dataclass +class LoaderConfig: + base_url: str = "http://localhost:8080" + batch_size: int = 50 # used for actuals ingest + delay_between_batches: float = 0.05 + dry_run: bool = False + auth_token: Optional[str] = None + +DEFAULT_CONFIG = LoaderConfig() + +# ── CSV reading helpers ─────────────────────────────────────────────────────── +INT_FIELDS = {"year", "month"} +FLOAT_FIELDS = { + "budget_amount", "actual_amount", "variance", "variance_pct", + "product_revenue", "service_revenue", "total_revenue", + "cogs_product", "cogs_service", "total_cogs", + "gross_profit", "gross_margin_pct", "total_opex", + "ebitda", "ebitda_margin_pct", "net_income", + "cash_collected_product", "cash_collected_service", + "cash_paid_opex", "cash_paid_cogs", "net_operating_cash_flow", + "capex", "net_investing_cash_flow", "loan_repayment", + "equity_raised", "net_financing_cash_flow", + "net_change_in_cash", "closing_cash_balance", + "annual_salary_budget", "actual_salary_paid_ytd", "headcount_fte", +} + +def _coerce(row: Dict[str, str]) -> Dict[str, Any]: + out = {} + for k, v in row.items(): + if k in INT_FIELDS: + out[k] = int(v) if v else None + elif k in FLOAT_FIELDS: + out[k] = float(v) if v else None + else: + out[k] = v + return out + +def read_csv(filename: str) -> List[Dict[str, Any]]: + path = os.path.join(DATA_DIR, filename) + if not os.path.exists(path): + raise FileNotFoundError(f"CSV not found: {path} (run generators/generate_data.py first)") + with open(path, newline="") as f: + return [_coerce(row) for row in csv.DictReader(f)] + +# ── Payload mappers ─────────────────────────────────────────────────────────── +# Each mapper takes one CSV row and returns the JSON body your Go handler expects. +# Adjust field names here if your Go structs use different names. + +def revenue_row_to_budget(row: Dict) -> Dict: + """revenue_budget_vs_actuals.csv → POST /api/v1/budgets""" + return { + "category": row["revenue_type"], # "Product" | "Service" + "department": "Revenue", + "period": row["period"], # "2023-01" + "year": row["year"], + "month": row["month"], + "amount": row["budget_amount"], + "currency": "USD", + "notes": f"{row['revenue_type']} revenue budget", + } + +def revenue_row_to_actual(row: Dict) -> Dict: + """revenue_budget_vs_actuals.csv → POST /api/v1/actuals/ingest""" + return { + "category": row["revenue_type"], + "department": "Revenue", + "period": row["period"], + "year": row["year"], + "month": row["month"], + "amount": row["actual_amount"], + "currency": "USD", + "source": "csv_import", + } + +def opex_row_to_budget(row: Dict) -> Dict: + """opex_budget_vs_actuals.csv → POST /api/v1/budgets""" + return { + "category": row["category"], # "Salaries", "Cloud Infrastructure", … + "department": row["department"], + "period": row["period"], + "year": row["year"], + "month": row["month"], + "amount": row["budget_amount"], + "currency": "USD", + "notes": f"{row['department']} opex budget", + } + +def opex_row_to_actual(row: Dict) -> Dict: + """opex_budget_vs_actuals.csv → POST /api/v1/actuals/ingest""" + return { + "category": row["category"], + "department": row["department"], + "period": row["period"], + "year": row["year"], + "month": row["month"], + "amount": row["actual_amount"], + "currency": "USD", + "source": "csv_import", + } + +def pl_row_to_actual(row: Dict) -> Dict: + """ + pl_income_statement.csv → POST /api/v1/actuals/ingest + The P&L is a derived/summary view; we ingest key line items as actuals. + Budgets for these are already covered by revenue + opex CSVs. + """ + return { + "category": "P&L Summary", + "department": "Finance", + "period": row["period"], + "year": row["year"], + "month": row["month"], + "amount": row["net_income"], + "currency": "USD", + "source": "csv_import", + "metadata": { + "total_revenue": row["total_revenue"], + "gross_profit": row["gross_profit"], + "gross_margin_pct": row["gross_margin_pct"], + "ebitda": row["ebitda"], + "ebitda_margin_pct": row["ebitda_margin_pct"], + }, + } + +def cashflow_row_to_actual(row: Dict) -> Dict: + """cash_flow.csv → POST /api/v1/actuals/ingest""" + return { + "category": "Cash Flow", + "department": "Finance", + "period": row["period"], + "year": row["year"], + "month": row["month"], + "amount": row["net_change_in_cash"], + "currency": "USD", + "source": "csv_import", + "metadata": { + "net_operating_cash_flow": row["net_operating_cash_flow"], + "net_investing_cash_flow": row["net_investing_cash_flow"], + "net_financing_cash_flow": row["net_financing_cash_flow"], + "closing_cash_balance": row["closing_cash_balance"], + "equity_raised": row["equity_raised"], + }, + } + +def headcount_row_to_actual(row: Dict) -> Dict: + """headcount_workforce.csv → POST /api/v1/actuals/ingest (active employees only)""" + return { + "category": "Headcount", + "department": row["department"], + "period": row["period"], + "year": row["year"], + "month": row["month"], + "amount": row["actual_salary_paid_ytd"], + "currency": "USD", + "source": "csv_import", + "metadata": { + "employee_id": row["employee_id"], + "role": row["role"], + "status": row["status"], + "fte": row["headcount_fte"], + "annual_salary": row["annual_salary_budget"], + }, + } + +# ── HTTP helpers ────────────────────────────────────────────────────────────── +def _headers(config: LoaderConfig) -> Dict: + h = {"Content-Type": "application/json"} + if config.auth_token: + h["Authorization"] = f"Bearer {config.auth_token}" + return h + +def post_one(url: str, payload: Dict, config: LoaderConfig) -> Dict: + """Single POST — used for /api/v1/budgets (one record per call).""" + if config.dry_run: + print(f" [DRY RUN] POST {url}") + print(f" {json.dumps(payload, indent=6)}") + return {"status": "dry_run"} + try: + resp = requests.post(url, json=payload, headers=_headers(config), timeout=10) + resp.raise_for_status() + return resp.json() if resp.content else {"status": "ok"} + except requests.exceptions.ConnectionError: + return {"error": "connection_refused"} + except requests.exceptions.HTTPError as e: + return {"error": str(e), "status_code": resp.status_code, "body": resp.text} + except Exception as e: + return {"error": str(e)} + +def post_batch(url: str, records: List[Dict], config: LoaderConfig) -> Dict: + """Batch POST — used for /api/v1/actuals/ingest.""" + if config.dry_run: + print(f" [DRY RUN] POST {url} ({len(records)} records)") + print(f" Sample: {json.dumps(records[0], indent=6)}") + return {"status": "dry_run", "count": len(records)} + try: + resp = requests.post(url, json={"records": records}, + headers=_headers(config), timeout=30) + resp.raise_for_status() + return resp.json() if resp.content else {"status": "ok"} + except requests.exceptions.ConnectionError: + return {"error": "connection_refused"} + except requests.exceptions.HTTPError as e: + return {"error": str(e), "status_code": resp.status_code, "body": resp.text} + except Exception as e: + return {"error": str(e)} + +# ── Budget loader: POST /api/v1/budgets ─────────────────────────────────────── +def load_budgets(config: LoaderConfig = DEFAULT_CONFIG) -> Dict: + """ + Loads budget rows from revenue + opex CSVs. + Each row is a separate POST (budget records are individual, not batched). + Deduplicated by (category, department, period) to avoid double-posting. + """ + url = config.base_url.rstrip("/") + "/api/v1/budgets" + budget_rows = [] + + for filename, mapper in [ + ("revenue_budget_vs_actuals.csv", revenue_row_to_budget), + ("opex_budget_vs_actuals.csv", opex_row_to_budget), + ]: + rows = read_csv(filename) + budget_rows.extend(mapper(r) for r in rows) + + # Deduplicate: last write wins for same category+dept+period + seen = {} + for row in budget_rows: + key = (row["category"], row["department"], row["period"]) + seen[key] = row + unique = list(seen.values()) + + print(f"\n📋 Loading budgets → {url}") + print(f" {len(unique)} unique budget lines") + + results = {"total": len(unique), "ok": 0, "errors": []} + for i, payload in enumerate(unique, 1): + result = post_one(url, payload, config) + if "error" in result: + results["errors"].append({**result, "payload": payload}) + if len(results["errors"]) <= 3: # don't flood the console + print(f" ⚠ [{i}/{len(unique)}] {result['error']}") + else: + results["ok"] += 1 + if i % 50 == 0 or i == len(unique): + print(f" ✓ {i}/{len(unique)} budgets posted") + time.sleep(0.01) # light throttle for individual POSTs + + return results + +# ── Actuals loader: POST /api/v1/actuals/ingest ─────────────────────────────── +def load_actuals(config: LoaderConfig = DEFAULT_CONFIG) -> Dict: + """ + Collects actuals from all CSV sources and bulk-ingests them. + Filters headcount to Active employees only to avoid salary double-counting. + """ + url = config.base_url.rstrip("/") + "/api/v1/actuals/ingest" + all_actuals = [] + + # Revenue actuals + for row in read_csv("revenue_budget_vs_actuals.csv"): + all_actuals.append(revenue_row_to_actual(row)) + + # Opex actuals + for row in read_csv("opex_budget_vs_actuals.csv"): + all_actuals.append(opex_row_to_actual(row)) + + # P&L summary actuals + for row in read_csv("pl_income_statement.csv"): + all_actuals.append(pl_row_to_actual(row)) + + # Cash flow actuals + for row in read_csv("cash_flow.csv"): + all_actuals.append(cashflow_row_to_actual(row)) + + # Headcount actuals — active employees only, one record per employee per month + headcount_seen = set() + for row in read_csv("headcount_workforce.csv"): + if row["status"] != "Active": + continue + key = (row["employee_id"], row["period"]) + if key in headcount_seen: + continue + headcount_seen.add(key) + all_actuals.append(headcount_row_to_actual(row)) + + print(f"\n📥 Loading actuals → {url}") + print(f" {len(all_actuals)} records | batch size {config.batch_size}") + + results = {"total": len(all_actuals), "batches": 0, "errors": []} + for i in range(0, len(all_actuals), config.batch_size): + batch = all_actuals[i : i + config.batch_size] + result = post_batch(url, batch, config) + results["batches"] += 1 + if "error" in result: + results["errors"].append(result) + print(f" ⚠ batch {results['batches']}: {result['error']}") + else: + print(f" ✓ batch {results['batches']} ({len(batch)} records)") + time.sleep(config.delay_between_batches) + + return results + +# ── Variance check: GET /api/v1/variance ────────────────────────────────────── +def check_variance(config: LoaderConfig = DEFAULT_CONFIG, period: Optional[str] = None): + """ + Calls the variance report after loading to verify data landed correctly. + """ + url = config.base_url.rstrip("/") + "/api/v1/variance" + params = {"period": period} if period else {} + print(f"\n📊 Fetching variance report → {url}") + if config.dry_run: + print(" [DRY RUN] skipped") + return {} + try: + resp = requests.get(url, params=params, timeout=10) + resp.raise_for_status() + data = resp.json() + print(f" ✓ {len(data) if isinstance(data, list) else 1} variance entries returned") + return data + except Exception as e: + print(f" ⚠ {e}") + return {"error": str(e)} + +def check_alerts(config: LoaderConfig = DEFAULT_CONFIG): + """Calls /api/v1/variance/alerts — shows any over/under budget flags.""" + url = config.base_url.rstrip("/") + "/api/v1/variance/alerts" + print(f"\n🚨 Fetching variance alerts → {url}") + if config.dry_run: + print(" [DRY RUN] skipped") + return {} + try: + resp = requests.get(url, timeout=10) + resp.raise_for_status() + data = resp.json() + count = len(data) if isinstance(data, list) else "?" + print(f" ✓ {count} alerts") + return data + except Exception as e: + print(f" ⚠ {e}") + return {"error": str(e)} + +# ── Full seed run ───────────────────────────────────────────────────────────── +def seed_all(config: LoaderConfig = DEFAULT_CONFIG): + """ + Full seed sequence: + 1. Load budgets (POST /api/v1/budgets, one at a time) + 2. Load actuals (POST /api/v1/actuals/ingest, batched) + 3. Spot-check variance report + """ + print(f"\n🚀 FP&A Seed → {config.base_url}") + if config.dry_run: + print(" *** DRY RUN — no requests will be sent ***\n") + + budget_result = load_budgets(config) + actuals_result = load_actuals(config) + check_variance(config) + check_alerts(config) + + b_errors = len(budget_result.get("errors", [])) + a_errors = len(actuals_result.get("errors", [])) + + print("\n── Seed Summary ─────────────────────────────────") + print(f" Budgets : {budget_result['ok']}/{budget_result['total']} ok" + + (f" ⚠ {b_errors} errors" if b_errors else " ✅")) + print(f" Actuals : {actuals_result['total']} records in " + f"{actuals_result['batches']} batches" + + (f" ⚠ {a_errors} errors" if a_errors else " ✅")) + print() + return {"budgets": budget_result, "actuals": actuals_result} + +# ── CLI ─────────────────────────────────────────────────────────────────────── +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Seed FP&A CSV data into Go API") + parser.add_argument("--url", default="http://localhost:8080", help="API base URL") + parser.add_argument("--batch", type=int, default=50, help="Actuals batch size") + parser.add_argument("--dry-run", action="store_true", help="Print payloads, don't send") + parser.add_argument("--token", default=None, help="Bearer auth token") + parser.add_argument("--only", choices=["budgets", "actuals", "variance", "alerts"], + help="Run only one step instead of full seed") + args = parser.parse_args() + + cfg = LoaderConfig( + base_url=args.url, + batch_size=args.batch, + dry_run=args.dry_run, + auth_token=args.token, + ) + + if args.only == "budgets": + load_budgets(cfg) + elif args.only == "actuals": + load_actuals(cfg) + elif args.only == "variance": + print(json.dumps(check_variance(cfg), indent=2)) + elif args.only == "alerts": + print(json.dumps(check_alerts(cfg), indent=2)) + else: + seed_all(cfg) \ No newline at end of file diff --git a/testing/requirements.txt b/testing/requirements.txt new file mode 100644 index 0000000..0e3d8c0 --- /dev/null +++ b/testing/requirements.txt @@ -0,0 +1,10 @@ +# FP&A Test Platform dependencies + +# HTTP client — used by loaders/api_loader.py to POST to the Go API +requests>=2.31.0,<3.0.0 + +# Needed by requests on some systems (usually installed transitively) +certifi>=2023.7.22 +urllib3>=1.26.0,<3.0.0 + +# Note: tests/test_fpa.py uses stdlib only (csv, urllib, json) — no extra deps needed \ No newline at end of file diff --git a/testing/tests/test_fpa.py b/testing/tests/test_fpa.py new file mode 100644 index 0000000..d857fac --- /dev/null +++ b/testing/tests/test_fpa.py @@ -0,0 +1,709 @@ +""" +FP&A Test Suite — stdlib only, no pytest +Run: python tests/test_fpa.py + python tests/test_fpa.py --api # include live API tests + python tests/test_fpa.py --url http://localhost:9000 +""" + +import csv +import json +import os +import sys +import traceback +import urllib.request +import urllib.error +from dataclasses import dataclass, field +from typing import Callable, List, Optional + +DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data", "csv") +API_BASE = os.getenv("FPA_API_URL", "http://localhost:8080") + +# ── Tiny test runner ────────────────────────────────────────────────────────── + +@dataclass +class Result: + name: str + passed: bool + skipped: bool = False + message: str = "" + +class Suite: + def __init__(self, name: str): + self.name = name + self.results: List[Result] = [] + + def run(self, label: str, fn: Callable): + try: + fn() + self.results.append(Result(label, passed=True)) + except SkipTest as e: + self.results.append(Result(label, passed=False, skipped=True, message=str(e))) + except AssertionError as e: + self.results.append(Result(label, passed=False, message=str(e))) + except Exception as e: + self.results.append(Result(label, passed=False, + message=f"{type(e).__name__}: {e}\n{traceback.format_exc()}")) + +class SkipTest(Exception): + pass + +def skip(reason: str): + raise SkipTest(reason) + +# ── CSV helper ──────────────────────────────────────────────────────────────── + +def read_csv(filename: str) -> List[dict]: + path = os.path.join(DATA_DIR, filename) + if not os.path.exists(path): + skip(f"{filename} not found — run generators/generate_data.py first") + with open(path, newline="") as f: + return list(csv.DictReader(f)) + +# ── HTTP helpers (stdlib only) ──────────────────────────────────────────────── + +def http_get(url: str, timeout: int = 5): + try: + with urllib.request.urlopen(url, timeout=timeout) as resp: + body = json.loads(resp.read().decode()) + return resp.status, body + except urllib.error.HTTPError as e: + try: + body = json.loads(e.read().decode()) + except Exception: + body = {} + return e.code, body + except Exception: + return 0, {} + +def http_post(url: str, payload: dict, timeout: int = 10): + data = json.dumps(payload).encode() + req = urllib.request.Request(url, data=data, + headers={"Content-Type": "application/json"}, + method="POST") + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: + raw = resp.read() + body = json.loads(raw.decode()) if raw else {} + return resp.status, body + except urllib.error.HTTPError as e: + try: + body = json.loads(e.read().decode()) + except Exception: + body = {} + return e.code, body + except Exception: + return 0, {} + +def http_put(url: str, payload: dict, timeout: int = 10): + data = json.dumps(payload).encode() + req = urllib.request.Request(url, data=data, + headers={"Content-Type": "application/json"}, + method="PUT") + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: + raw = resp.read() + body = json.loads(raw.decode()) if raw else {} + return resp.status, body + except urllib.error.HTTPError as e: + return e.code, {} + except Exception: + return 0, {} + +def api_available() -> bool: + status, _ = http_get(f"{API_BASE}/api/v1/health", timeout=2) + return status == 200 + +# ── Assertion helpers ───────────────────────────────────────────────────────── + +def assert_true(condition, message: str = ""): + if not condition: + raise AssertionError(message or "Expected True, got False") + +def assert_eq(actual, expected, message: str = ""): + if actual != expected: + raise AssertionError(message or f"Expected {expected!r}, got {actual!r}") + +# ── Reference data ─────────────────────────────────────────────────────────── +# +# Departments and GL accounts are CREATED via the API before any budget/actual. +# seed_reference_data() POSTs them and stores returned auto-increment IDs into +# DEPARTMENT_IDS and GL_ACCOUNTS at runtime — nothing is hardcoded. +# Uses ON CONFLICT upsert on the Go side so re-runs are safe. + +BUDGET_VERSION = "v1" # matches BudgetVersion enum in your Go service + +# Runtime ID maps — populated by seed_reference_data() +DEPARTMENT_IDS: dict = {} +GL_ACCOUNTS: dict = {} # category name → {"id": int, "code": str} + +_DEPARTMENT_DEFS = [ + # code, name, cost_center — matches departments table exactly + {"code": "REV", "name": "Revenue", "cost_center": "CC-100", "active": True}, + {"code": "ENG", "name": "Engineering", "cost_center": "CC-200", "active": True}, + {"code": "SAL", "name": "Sales", "cost_center": "CC-300", "active": True}, + {"code": "MKT", "name": "Marketing", "cost_center": "CC-400", "active": True}, + {"code": "OPS", "name": "Operations", "cost_center": "CC-500", "active": True}, + {"code": "FIN", "name": "Finance", "cost_center": "CC-600", "active": True}, +] + +_GL_ACCOUNT_DEFS = [ + # code, description, type (revenue|cogs|opex|capex|headcount), favour_high + # favour_high=True means over-budget is good (i.e. revenue beating target) + # Revenue + {"code": "4000", "description": "SaaS Subscription Revenue", "type": "revenue", "favour_high": True}, + {"code": "4100", "description": "Professional Services Revenue","type": "revenue", "favour_high": True}, + # COGS + {"code": "5000", "description": "Cost of Goods — Product", "type": "cogs", "favour_high": False}, + {"code": "5100", "description": "Cost of Goods — Service", "type": "cogs", "favour_high": False}, + # Opex + {"code": "6000", "description": "Salaries and Wages", "type": "opex", "favour_high": False}, + {"code": "6100", "description": "Software and SaaS Tools", "type": "opex", "favour_high": False}, + {"code": "6200", "description": "Travel and Expenses", "type": "opex", "favour_high": False}, + {"code": "6300", "description": "Marketing and Paid Media", "type": "opex", "favour_high": False}, + {"code": "6400", "description": "Cloud Infrastructure", "type": "opex", "favour_high": False}, + {"code": "6500", "description": "Contractors and Freelancers","type": "opex", "favour_high": False}, + {"code": "6600", "description": "Office and Facilities", "type": "opex", "favour_high": False}, + # Capex + {"code": "7000", "description": "Capital Expenditure", "type": "capex", "favour_high": False}, + # Headcount + {"code": "9200", "description": "Headcount Cost", "type": "headcount", "favour_high": False}, +] + +# Maps CSV category names → GL codes so the seeder can find the right account +_CSV_CATEGORY_TO_GL_CODE = { + "Product": "4000", + "Service": "4100", + "Salaries": "6000", + "Software & Tools": "6100", + "Travel": "6200", + "Marketing Spend": "6300", + "Cloud Infrastructure": "6400", + "Contractors": "6500", + "Office & Facilities": "6600", + "P&L Summary": "4000", # roll up to revenue GL for summary lines + "Cash Flow": "7000", + "Headcount": "9200", +} + +def seed_reference_data() -> Optional[str]: + """ + POST all departments and GL accounts to the API and store returned IDs. + Returns an error string on first failure, or None on success. + Safe to call multiple times — Go side uses ON CONFLICT(code) upsert. + + Populates: + DEPARTMENT_IDS — {"Engineering": 3, ...} keyed by department name + GL_ACCOUNTS — {"4000": {"id": 1, "code": "4000"}, ...} keyed by GL code + """ + for defn in _DEPARTMENT_DEFS: + status, body = http_post(f"{API_BASE}/api/v1/departments", defn) + if status not in (200, 201): + return f"POST /departments '{defn['name']}' failed ({status}): {body}" + DEPARTMENT_IDS[defn["name"]] = body["id"] + + for defn in _GL_ACCOUNT_DEFS: + status, body = http_post(f"{API_BASE}/api/v1/gl-accounts", defn) + if status not in (200, 201): + return f"POST /gl-accounts '{defn['code']}' failed ({status}): {body}" + GL_ACCOUNTS[defn["code"]] = {"id": body["id"], "code": defn["code"]} + + return None # success + +def gl(category: str) -> dict: + """ + Resolve a CSV category name to a seeded GL account dict {"id": int, "code": str}. + Looks up via _CSV_CATEGORY_TO_GL_CODE then into the runtime GL_ACCOUNTS map. + """ + code = _CSV_CATEGORY_TO_GL_CODE.get(category) + if code is None: + raise AssertionError( + f"No GL code mapping for CSV category '{category}'. " + f"Add it to _CSV_CATEGORY_TO_GL_CODE." + ) + acct = GL_ACCOUNTS.get(code) + if acct is None: + raise AssertionError( + f"GL code '{code}' (for '{category}') not in runtime map — " + f"was seed_reference_data() called? Known codes: {list(GL_ACCOUNTS.keys())}" + ) + return acct + +def period_to_fiscal(period: str) -> tuple: + """'2023-04' → (2023, 4)""" + year, month = period.split("-") + return int(year), int(month) + +# ── suite_revenue — full API round-trip ─────────────────────────────────────── +# +# Flow: +# 1. Load CSV rows (local data validation) +# 2. POST each row as a CreateBudgetRequest → /api/v1/budgets +# 3. POST actuals for the same rows → /api/v1/actuals/ingest +# 4. GET /api/v1/variance → verify amounts round-trip correctly + +def suite_revenue() -> Suite: + """ + Maps to Go structs: + + CreateBudgetRequest: + fiscal_year, fiscal_period, version, department_id, + gl_account_id, amount, currency, notes, created_by + + Actual (ingest): + fiscal_year, fiscal_period, department_id, + gl_account_id, gl_code, amount, currency, source + """ + s = Suite("Revenue — CSV + API round-trip") + + if not api_available(): + s.run("API reachable", lambda: skip(f"Go API not running at {API_BASE}")) + return s + + rows = [] + + # ── Step 1: load and validate CSV data ──────────────────────────────────── + + def load(): + rows.extend(read_csv("revenue_budget_vs_actuals.csv")) + s.run("CSV loads without error", load) + + s.run("CSV has 48 rows (24 months × 2 revenue types)", lambda: + assert_eq(len(rows), 48)) + + s.run("CSV revenue types are Product and Service only", lambda: + assert_eq({r["revenue_type"] for r in rows}, {"Product", "Service"})) + + def check_csv_variance(): + for r in rows: + diff = float(r["actual_amount"]) - float(r["budget_amount"]) + assert_true(abs(diff - float(r["variance"])) < 0.01, + f"CSV variance mismatch in {r['period']}") + s.run("CSV variance = actual − budget", check_csv_variance) + + # ── Step 2: seed departments + GL accounts ──────────────────────────────── + # Must succeed before any budget POST — these rows are the FK parents. + + def seed_refs(): + err = seed_reference_data() + assert_true(err is None, err or "seed_reference_data failed") + s.run("POST /api/v1/departments + /api/v1/gl-accounts (seed FK parents)", seed_refs) + + # ── Step 3: POST budgets ────────────────────────────────────────────────── + # One CreateBudgetRequest per CSV row. + # We only post the first period (2023-01) for both types to keep the + # test focused; the loader handles the full dataset. + + budget_ids: dict[str, int] = {} # key: "revenue_type:period" → returned id + + def post_product_budget(): + row = next(r for r in rows + if r["revenue_type"] == "Product" and r["period"] == "2023-01") + fy, fp = period_to_fiscal(row["period"]) + payload = { + "fiscal_year": fy, + "fiscal_period": fp, + "version": BUDGET_VERSION, + "department_id": DEPARTMENT_IDS["Revenue"], + "gl_account_id": gl("Product")["id"], + "amount": float(row["budget_amount"]), + "currency": "USD", + "notes": "Product revenue budget — csv import", + "created_by": "test_suite", + } + status, body = http_post(f"{API_BASE}/api/v1/budgets", payload) + assert_true(status in (200, 201), + f"POST /budgets failed ({status}): {body}") + budget_id = body.get("id") + assert_true(budget_id is not None, "Response missing 'id' field") + budget_ids["Product:2023-01"] = budget_id + + s.run("POST /api/v1/budgets — Product revenue 2023-01", post_product_budget) + + def post_service_budget(): + row = next(r for r in rows + if r["revenue_type"] == "Service" and r["period"] == "2023-01") + fy, fp = period_to_fiscal(row["period"]) + payload = { + "fiscal_year": fy, + "fiscal_period": fp, + "version": BUDGET_VERSION, + "department_id": DEPARTMENT_IDS["Revenue"], + "gl_account_id": gl("Service")["id"], + "amount": float(row["budget_amount"]), + "currency": "USD", + "notes": "Service revenue budget — csv import", + "created_by": "test_suite", + } + status, body = http_post(f"{API_BASE}/api/v1/budgets", payload) + assert_true(status in (200, 201), + f"POST /budgets failed ({status}): {body}") + budget_ids["Service:2023-01"] = body.get("id") + + s.run("POST /api/v1/budgets — Service revenue 2023-01", post_service_budget) + + # ── Step 4: POST actuals ────────────────────────────────────────────────── + # Ingest actual amounts for the same period so variance can be computed. + + ingested_actuals: list[dict] = [] + + def post_actuals(): + period_rows = [r for r in rows if r["period"] == "2023-01"] + records = [] + for r in period_rows: + fy, fp = period_to_fiscal(r["period"]) + account = gl(r["revenue_type"]) + records.append({ + "fiscal_year": fy, + "fiscal_period": fp, + "department_id": DEPARTMENT_IDS["Revenue"], + "gl_account_id": account["id"], + "gl_code": account["code"], + "amount": float(r["actual_amount"]), + "currency": "USD", + "source": "test_suite_csv", + }) + + status, body = http_post(f"{API_BASE}/api/v1/actuals/ingest", + {"records": records}) + assert_true(status in (200, 201), + f"POST /actuals/ingest failed ({status}): {body}") + + # Store returned actuals for later assertions + returned = body.get("actuals") or body.get("records") or [] + ingested_actuals.extend(returned) + + s.run("POST /api/v1/actuals/ingest — Product + Service 2023-01", post_actuals) + + # ── Step 5: GET variance and verify numbers ─────────────────────────────── + + variance_data: list[dict] = [] + + def fetch_variance(): + url = f"{API_BASE}/api/v1/variance?fiscal_year=2023&fiscal_period=1" + status, body = http_get(url) + assert_true(status == 200, f"GET /variance failed ({status}): {body}") + # API may return a list directly or wrapped in a key + items = body if isinstance(body, list) else body.get("variance") or body.get("data") or [] + assert_true(len(items) > 0, "Variance response is empty — data may not have landed") + variance_data.extend(items) + + s.run("GET /api/v1/variance returns entries for 2023-01", fetch_variance) + + def verify_product_variance(): + csv_row = next(r for r in rows + if r["revenue_type"] == "Product" and r["period"] == "2023-01") + expected_budget = float(csv_row["budget_amount"]) + expected_actual = float(csv_row["actual_amount"]) + expected_variance = float(csv_row["variance"]) + + # Find the matching variance entry by GL account id + entry = next( + (v for v in variance_data + if v.get("gl_account_id") == gl("Product")["id"] + or v.get("gl_code") == gl("Product")["code"]), + None + ) + assert_true(entry is not None, + f"No variance entry found for Product (GL {gl('Product')['id']})") + + api_budget = float(entry.get("budget_amount") or entry.get("budget") or 0) + api_actual = float(entry.get("actual_amount") or entry.get("actual") or 0) + api_variance = float(entry.get("variance") or entry.get("variance_amount") or 0) + + assert_true(abs(api_budget - expected_budget) < 1.0, + f"Budget mismatch: API={api_budget} CSV={expected_budget}") + assert_true(abs(api_actual - expected_actual) < 1.0, + f"Actual mismatch: API={api_actual} CSV={expected_actual}") + assert_true(abs(api_variance - expected_variance) < 1.0, + f"Variance mismatch: API={api_variance} CSV={expected_variance}") + + s.run("Variance amounts match CSV for Product 2023-01", verify_product_variance) + + def verify_service_variance(): + csv_row = next(r for r in rows + if r["revenue_type"] == "Service" and r["period"] == "2023-01") + expected_budget = float(csv_row["budget_amount"]) + expected_actual = float(csv_row["actual_amount"]) + expected_variance = float(csv_row["variance"]) + + entry = next( + (v for v in variance_data + if v.get("gl_account_id") == gl("Service")["id"] + or v.get("gl_code") == gl("Service")["code"]), + None + ) + assert_true(entry is not None, + f"No variance entry found for Service (GL {gl('Service')['id']})") + + api_budget = float(entry.get("budget_amount") or entry.get("budget") or 0) + api_actual = float(entry.get("actual_amount") or entry.get("actual") or 0) + api_variance = float(entry.get("variance") or entry.get("variance_amount") or 0) + + assert_true(abs(api_budget - expected_budget) < 1.0, + f"Budget mismatch: API={api_budget} CSV={expected_budget}") + assert_true(abs(api_actual - expected_actual) < 1.0, + f"Actual mismatch: API={api_actual} CSV={expected_actual}") + assert_true(abs(api_variance - expected_variance) < 1.0, + f"Variance mismatch: API={api_variance} CSV={expected_variance}") + + s.run("Variance amounts match CSV for Service 2023-01", verify_service_variance) + + # ── Step 6: update a budget and verify variance shifts ──────────────────── + + def update_and_reverify(): + budget_id = budget_ids.get("Product:2023-01") + if not budget_id: + skip("Product budget id not captured — skipping update test") + + csv_row = next(r for r in rows + if r["revenue_type"] == "Product" and r["period"] == "2023-01") + revised_amount = float(csv_row["budget_amount"]) * 1.10 # +10% revision + + fy, fp = period_to_fiscal("2023-01") + status, body = http_put( + f"{API_BASE}/api/v1/budgets/{budget_id}", + { + "fiscal_year": fy, + "fiscal_period": fp, + "version": BUDGET_VERSION, + "department_id": DEPARTMENT_IDS["Revenue"], + "gl_account_id": gl("Product")["id"], + "amount": revised_amount, + "currency": "USD", + "notes": "revised +10% mid-cycle", + "created_by": "test_suite", + } + ) + assert_true(status in (200, 204), + f"PUT /budgets/{budget_id} failed ({status}): {body}") + + # Re-fetch variance — the variance amount should now reflect the new budget + url = f"{API_BASE}/api/v1/variance?fiscal_year=2023&fiscal_period=1" + status2, body2 = http_get(url) + assert_true(status2 == 200, f"Re-fetch variance failed ({status2})") + + items = body2 if isinstance(body2, list) else body2.get("variance") or body2.get("data") or [] + entry = next( + (v for v in items if v.get("gl_account_id") == gl("Product")["id"] + or v.get("gl_code") == gl("Product")["code"]), + None + ) + assert_true(entry is not None, "Product variance entry missing after budget update") + + api_budget = float(entry.get("budget_amount") or entry.get("budget") or 0) + assert_true(abs(api_budget - revised_amount) < 1.0, + f"Updated budget not reflected: API={api_budget} expected={revised_amount:.2f}") + + s.run("PUT /api/v1/budgets/{id} — variance reflects revised budget", update_and_reverify) + + return s + + +# ── Remaining CSV-only suites (unchanged) ───────────────────────────────────── + +def suite_opex() -> Suite: + s = Suite("Opex CSV") + rows = [] + + def load(): + rows.extend(read_csv("opex_budget_vs_actuals.csv")) + s.run("loads without error", load) + + s.run("has rows", lambda: assert_true(len(rows) > 0)) + + s.run("all departments present", lambda: + assert_true( + {"Engineering", "Sales", "Marketing", "Operations"}.issubset( + {r["department"] for r in rows} + ))) + + def check_no_zero(): + for r in rows: + assert_true(float(r["budget_amount"]) > 0, + f"Zero budget: {r['department']} / {r['category']}") + s.run("no zero budget amounts", check_no_zero) + + def check_sign(): + for r in rows: + actual = float(r["actual_amount"]) + budget = float(r["budget_amount"]) + variance = float(r["variance"]) + exp = 1 if actual > budget else (-1 if actual < budget else 0) + got = 1 if variance > 0 else (-1 if variance < 0 else 0) + assert_eq(got, exp, f"Variance sign wrong in {r['period']} {r['category']}") + s.run("variance sign matches actual vs budget", check_sign) + + return s + + +def suite_pl() -> Suite: + s = Suite("P&L CSV") + rows = [] + + def load(): + rows.extend(read_csv("pl_income_statement.csv")) + s.run("loads without error", load) + + s.run("exactly 24 rows", lambda: assert_eq(len(rows), 24)) + + def check_rev_sum(): + for r in rows: + total = float(r["product_revenue"]) + float(r["service_revenue"]) + assert_true(abs(total - float(r["total_revenue"])) < 0.05, + f"Revenue sum mismatch in {r['period']}") + s.run("total_revenue = product + service", check_rev_sum) + + def check_gross(): + for r in rows: + gp = float(r["total_revenue"]) - float(r["total_cogs"]) + assert_true(abs(gp - float(r["gross_profit"])) < 0.05, + f"Gross profit mismatch in {r['period']}") + s.run("gross_profit = revenue − cogs", check_gross) + + def check_margin(): + for r in rows: + gm = float(r["gross_margin_pct"]) + assert_true(30 <= gm <= 90, f"Gross margin {gm}% out of range in {r['period']}") + s.run("gross margin between 30% and 90%", check_margin) + + def check_ebitda(): + for r in rows: + ebitda = float(r["gross_profit"]) - float(r["total_opex"]) + assert_true(abs(ebitda - float(r["ebitda"])) < 0.05, + f"EBITDA mismatch in {r['period']}") + s.run("ebitda = gross_profit − opex", check_ebitda) + + return s + + +def suite_cashflow() -> Suite: + s = Suite("Cash Flow CSV") + rows = [] + + def load(): + rows.extend(read_csv("cash_flow.csv")) + s.run("loads without error", load) + + s.run("exactly 24 rows", lambda: assert_eq(len(rows), 24)) + + def check_net(): + for r in rows: + total = (float(r["net_operating_cash_flow"]) + + float(r["net_investing_cash_flow"]) + + float(r["net_financing_cash_flow"])) + assert_true(abs(total - float(r["net_change_in_cash"])) < 0.05, + f"Cash flow sum mismatch in {r['period']}") + s.run("net_change = operating + investing + financing", check_net) + + def check_pre_series_a(): + for r in rows: + if r["period"] <= "2023-05": + assert_true(float(r["closing_cash_balance"]) > 0, + f"Negative cash too early: {r['period']}") + s.run("cash balance positive before Series A (pre 2023-06)", check_pre_series_a) + + def check_series_a(): + june = next((r for r in rows if r["period"] == "2023-06"), None) + assert_true(june is not None, "2023-06 row missing") + assert_true(float(june["equity_raised"]) > 0, "Series A not recorded in 2023-06") + s.run("Series A visible in 2023-06", check_series_a) + + return s + + +def suite_headcount() -> Suite: + s = Suite("Headcount CSV") + rows = [] + + def load(): + rows.extend(read_csv("headcount_workforce.csv")) + s.run("loads without error", load) + + s.run("has rows", lambda: assert_true(len(rows) > 0)) + + s.run("status only Active or Terminated", lambda: + assert_true({r["status"] for r in rows}.issubset({"Active", "Terminated"}))) + + def check_fte(): + for r in rows: + fte = float(r["headcount_fte"]) + assert_true(0 < fte <= 1.0, + f"FTE {fte} out of range for {r['employee_id']}") + s.run("FTE between 0 and 1.0", check_fte) + + def check_salary(): + for r in rows: + assert_true(float(r["annual_salary_budget"]) > 0) + s.run("all salaries positive", check_salary) + + def check_growth(): + jan = [r for r in rows if r["period"] == "2023-01" and r["status"] == "Active"] + dec = [r for r in rows if r["period"] == "2024-12" and r["status"] == "Active"] + assert_true(len(dec) >= len(jan), + f"Headcount shrank: {len(jan)} in Jan 2023 → {len(dec)} in Dec 2024") + s.run("headcount grows from 2023-01 to 2024-12", check_growth) + + return s + + +# ── Reporter ────────────────────────────────────────────────────────────────── + +PASS = "\033[32m✓\033[0m" +FAIL = "\033[31m✗\033[0m" +SKIP = "\033[33m⊘\033[0m" +BOLD = "\033[1m" +RESET = "\033[0m" + +def run_suites(suites: List[Suite]) -> bool: + total = passed = failed = skipped = 0 + + for suite in suites: + print(f"\n{BOLD}{suite.name}{RESET}") + for r in suite.results: + total += 1 + if r.skipped: + skipped += 1 + print(f" {SKIP} {r.name}") + print(f" {r.message}") + elif r.passed: + passed += 1 + print(f" {PASS} {r.name}") + else: + failed += 1 + print(f" {FAIL} {r.name}") + for line in r.message.strip().splitlines(): + print(f" {line}") + + print(f"\n{'─' * 48}") + color = "\033[32m" if failed == 0 else "\033[31m" + print(f"{color}{BOLD}{passed}/{total} passed{RESET}" + + (f" {SKIP} {skipped} skipped" if skipped else "") + + (f" {FAIL} {failed} failed" if failed else "")) + print() + return failed == 0 + +# ── Entry point ─────────────────────────────────────────────────────────────── + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="FP&A test suite — no dependencies needed") + parser.add_argument("--api", action="store_true", + help="Include revenue round-trip API test (requires Go API running)") + parser.add_argument("--url", default=None, help="Override API base URL") + args = parser.parse_args() + + if args.url: + API_BASE = args.url + + # suite_revenue always runs — it skips the API steps gracefully if not available + suites = [ + suite_revenue(), + suite_opex(), + suite_pl(), + suite_cashflow(), + suite_headcount(), + ] + + ok = run_suites(suites) + sys.exit(0 if ok else 1) \ No newline at end of file