some testing with python
This commit is contained in:
177
internal/database/refrence-repo.go
Normal file
177
internal/database/refrence-repo.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
144
internal/handler/refrence.go
Normal file
144
internal/handler/refrence.go
Normal file
@@ -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))
|
||||||
|
}
|
||||||
20
main.go
20
main.go
@@ -34,23 +34,39 @@ func main() {
|
|||||||
os.Exit(1)
|
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)
|
budgetRepo := database.NewBudgetRepo(db)
|
||||||
actualsRepo := database.NewActualsRepo(db)
|
actualsRepo := database.NewActualsRepo(db)
|
||||||
budgetSvc := service.NewBudgetService(budgetRepo)
|
budgetSvc := service.NewBudgetService(budgetRepo)
|
||||||
varianceSvc := service.NewVarianceService(budgetRepo, actualsRepo)
|
varianceSvc := service.NewVarianceService(budgetRepo, actualsRepo)
|
||||||
|
|
||||||
budgetH := handler.NewBudgetHandler(budgetSvc)
|
budgetH := handler.NewBudgetHandler(budgetSvc)
|
||||||
actualsH := handler.NewActualsHandler(actualsRepo)
|
actualsH := handler.NewActualsHandler(actualsRepo)
|
||||||
varianceH := handler.NewVarianceHandler(varianceSvc)
|
varianceH := handler.NewVarianceHandler(varianceSvc)
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
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("POST /api/v1/budgets", budgetH.Create)
|
||||||
mux.HandleFunc("PUT /api/v1/budgets/{id}", budgetH.Update)
|
mux.HandleFunc("PUT /api/v1/budgets/{id}", budgetH.Update)
|
||||||
mux.HandleFunc("DELETE /api/v1/budgets/{id}", budgetH.Delete)
|
mux.HandleFunc("DELETE /api/v1/budgets/{id}", budgetH.Delete)
|
||||||
|
|
||||||
|
// Actuals + variance
|
||||||
mux.HandleFunc("POST /api/v1/actuals/ingest", actualsH.Ingest)
|
mux.HandleFunc("POST /api/v1/actuals/ingest", actualsH.Ingest)
|
||||||
mux.HandleFunc("GET /api/v1/variance", varianceH.Report)
|
mux.HandleFunc("GET /api/v1/variance", varianceH.Report)
|
||||||
mux.HandleFunc("GET /api/v1/variance/alerts", varianceH.Alerts)
|
mux.HandleFunc("GET /api/v1/variance/alerts", varianceH.Alerts)
|
||||||
|
|
||||||
mux.HandleFunc("GET /api/v1/health", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("GET /api/v1/health", func(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := db.PingContext(r.Context()); err != nil {
|
if err := db.PingContext(r.Context()); err != nil {
|
||||||
http.Error(w, `{"error":"db unhealthy"}`, http.StatusServiceUnavailable)
|
http.Error(w, `{"error":"db unhealthy"}`, http.StatusServiceUnavailable)
|
||||||
|
|||||||
90
testing/README.md
Normal file
90
testing/README.md
Normal file
@@ -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
|
||||||
|
```
|
||||||
25
testing/data/csv/cash_flow.csv
Normal file
25
testing/data/csv/cash_flow.csv
Normal file
@@ -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
|
||||||
|
422
testing/data/csv/headcount_workforce.csv
Normal file
422
testing/data/csv/headcount_workforce.csv
Normal file
@@ -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
|
||||||
|
673
testing/data/csv/opex_budget_vs_actuals.csv
Normal file
673
testing/data/csv/opex_budget_vs_actuals.csv
Normal file
@@ -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
|
||||||
|
25
testing/data/csv/pl_income_statement.csv
Normal file
25
testing/data/csv/pl_income_statement.csv
Normal file
@@ -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
|
||||||
|
49
testing/data/csv/revenue_budget_vs_actuals.csv
Normal file
49
testing/data/csv/revenue_budget_vs_actuals.csv
Normal file
@@ -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
|
||||||
|
382
testing/generators/generate_data.py
Normal file
382
testing/generators/generate_data.py
Normal file
@@ -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")
|
||||||
428
testing/loader/api_loader.py
Normal file
428
testing/loader/api_loader.py
Normal file
@@ -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)
|
||||||
10
testing/requirements.txt
Normal file
10
testing/requirements.txt
Normal file
@@ -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
|
||||||
709
testing/tests/test_fpa.py
Normal file
709
testing/tests/test_fpa.py
Normal file
@@ -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)
|
||||||
Reference in New Issue
Block a user