// timeros/core/session.ts — one session per user, classified as it happens
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core";
import { classify } from "./on-device/model"; // bundled with the app
import { payroll } from "../billing/payroll";
export async function track(user: User): Promise<Session> {
const session = await Timer.start(user.id, { mode: "productive" });
session.on("activity", (event: ActivityEvent) => {
const kind = classify(event); // productive · idle · break
session.record(kind, event.duration); // rolled up per project
});
session.on("idle", () => session.pause());
return session.commit({ at: trustedNow(), tz: user.timezone });
}
// cost from payroll, revenue from the client rate → profit per project
export function settle(projects: Project[]): Money[] {
const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR" });
}
// the studio's own week runs through this same path
const week = sum(sessions, "productive"); // productive time only
const team = await Org.active(); // everyone in one workspace
export const live = stream(team, week, { realtime: true });
// timeros/core/session.ts — one session per user, classified as it happens
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core";
import { classify } from "./on-device/model"; // bundled with the app
import { payroll } from "../billing/payroll";
export async function track(user: User): Promise<Session> {
const session = await Timer.start(user.id, { mode: "productive" });
session.on("activity", (event: ActivityEvent) => {
const kind = classify(event); // productive · idle · break
session.record(kind, event.duration); // rolled up per project
});
session.on("idle", () => session.pause());
return session.commit({ at: trustedNow(), tz: user.timezone });
}
// cost from payroll, revenue from the client rate → profit per project
export function settle(projects: Project[]): Money[] {
const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR" });
}
// the studio's own week runs through this same path
const week = sum(sessions, "productive"); // productive time only
const team = await Org.active(); // everyone in one workspace
export const live = stream(team, week, { realtime: true });
// timeros/core/session.ts — one session per user, classified as it happens
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core";
import { classify } from "./on-device/model"; // bundled with the app
import { payroll } from "../billing/payroll";
export async function track(user: User): Promise<Session> {
const session = await Timer.start(user.id, { mode: "productive" });
session.on("activity", (event: ActivityEvent) => {
const kind = classify(event); // productive · idle · break
session.record(kind, event.duration); // rolled up per project
});
session.on("idle", () => session.pause());
return session.commit({ at: trustedNow(), tz: user.timezone });
}
// cost from payroll, revenue from the client rate → profit per project
export function settle(projects: Project[]): Money[] {
const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR" });
}
// the studio's own week runs through this same path
const week = sum(sessions, "productive"); // productive time only
const team = await Org.active(); // everyone in one workspace
export const live = stream(team, week, { realtime: true });
// timeros/core/session.ts — one session per user, classified as it happens
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core";
import { classify } from "./on-device/model"; // bundled with the app
import { payroll } from "../billing/payroll";
export async function track(user: User): Promise<Session> {
const session = await Timer.start(user.id, { mode: "productive" });
session.on("activity", (event: ActivityEvent) => {
const kind = classify(event); // productive · idle · break
session.record(kind, event.duration); // rolled up per project
});
session.on("idle", () => session.pause());
return session.commit({ at: trustedNow(), tz: user.timezone });
}
// cost from payroll, revenue from the client rate → profit per project
export function settle(projects: Project[]): Money[] {
const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR" });
}
// the studio's own week runs through this same path
const week = sum(sessions, "productive"); // productive time only
const team = await Org.active(); // everyone in one workspace
export const live = stream(team, week, { realtime: true });
// timeros/core/session.ts — one session per user, classified as it happens
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core";
import { classify } from "./on-device/model"; // bundled with the app
import { payroll } from "../billing/payroll";
export async function track(user: User): Promise<Session> {
const session = await Timer.start(user.id, { mode: "productive" });
session.on("activity", (event: ActivityEvent) => {
const kind = classify(event); // productive · idle · break
session.record(kind, event.duration); // rolled up per project
});
session.on("idle", () => session.pause());
return session.commit({ at: trustedNow(), tz: user.timezone });
}
// cost from payroll, revenue from the client rate → profit per project
export function settle(projects: Project[]): Money[] {
const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR" });
}
// the studio's own week runs through this same path
const week = sum(sessions, "productive"); // productive time only
const team = await Org.active(); // everyone in one workspace
export const live = stream(team, week, { realtime: true });
// timeros/core/session.ts — one session per user, classified as it happens
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core";
import { classify } from "./on-device/model"; // bundled with the app
import { payroll } from "../billing/payroll";
export async function track(user: User): Promise<Session> {
const session = await Timer.start(user.id, { mode: "productive" });
session.on("activity", (event: ActivityEvent) => {
const kind = classify(event); // productive · idle · break
session.record(kind, event.duration); // rolled up per project
});
session.on("idle", () => session.pause());
return session.commit({ at: trustedNow(), tz: user.timezone });
}
// cost from payroll, revenue from the client rate → profit per project
export function settle(projects: Project[]): Money[] {
const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR" });
}
// the studio's own week runs through this same path
const week = sum(sessions, "productive"); // productive time only
const team = await Org.active(); // everyone in one workspace
export const live = stream(team, week, { realtime: true });
// timeros/core/session.ts — one session per user, classified as it happens
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core";
import { classify } from "./on-device/model"; // bundled with the app
import { payroll } from "../billing/payroll";
export async function track(user: User): Promise<Session> {
const session = await Timer.start(user.id, { mode: "productive" });
session.on("activity", (event: ActivityEvent) => {
const kind = classify(event); // productive · idle · break
session.record(kind, event.duration); // rolled up per project
});
session.on("idle", () => session.pause());
return session.commit({ at: trustedNow(), tz: user.timezone });
}
// cost from payroll, revenue from the client rate → profit per project
export function settle(projects: Project[]): Money[] {
const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR" });
}
// the studio's own week runs through this same path
const week = sum(sessions, "productive"); // productive time only
const team = await Org.active(); // everyone in one workspace
export const live = stream(team, week, { realtime: true });
// timeros/core/session.ts — one session per user, classified as it happens
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core";
import { classify } from "./on-device/model"; // bundled with the app
import { payroll } from "../billing/payroll";
export async function track(user: User): Promise<Session> {
const session = await Timer.start(user.id, { mode: "productive" });
session.on("activity", (event: ActivityEvent) => {
const kind = classify(event); // productive · idle · break
session.record(kind, event.duration); // rolled up per project
});
session.on("idle", () => session.pause());
return session.commit({ at: trustedNow(), tz: user.timezone });
}
// cost from payroll, revenue from the client rate → profit per project
export function settle(projects: Project[]): Money[] {
const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR" });
}
// the studio's own week runs through this same path
const week = sum(sessions, "productive"); // productive time only
const team = await Org.active(); // everyone in one workspace
export const live = stream(team, week, { realtime: true });
// timeros — sessions are classified on the device, then rolled up into projects, payroll and invoices
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core"; import { classify } from "./on-device/model";
export async function track(user: User): Promise<Session> { const session = await Timer.start(user.id, { mode: "auto" });
session.on("activity", (event) => { const kind = classify(event); session.record(kind, event.duration); }); // classification stays on the machine
session.on("idle", () => session.pause()); return session.commit({ at: trustedNow(), tz: user.timezone, by: user.id });
}
// roll the week up into projects: cost comes from payroll, revenue from the client rate, profit falls out of the two
export function settle(projects: Project[]): Money[] { const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR", period: "month" }); } // run once a month
export async function invoice(client: Client) { const open = await Invoice.pending(client.id); return sum(open, "total"); }
const week = sum(sessions, "productive"); const team = await Org.active(); export const live = stream(team, week, { realtime: true });
// the studio runs entirely on this — one workspace for time, projects, clients, shifts, payroll and the client portal
// timeros — sessions are classified on the device, then rolled up into projects, payroll and invoices
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core"; import { classify } from "./on-device/model";
export async function track(user: User): Promise<Session> { const session = await Timer.start(user.id, { mode: "auto" });
session.on("activity", (event) => { const kind = classify(event); session.record(kind, event.duration); }); // classification stays on the machine
session.on("idle", () => session.pause()); return session.commit({ at: trustedNow(), tz: user.timezone, by: user.id });
}
// roll the week up into projects: cost comes from payroll, revenue from the client rate, profit falls out of the two
export function settle(projects: Project[]): Money[] { const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR", period: "month" }); } // run once a month
export async function invoice(client: Client) { const open = await Invoice.pending(client.id); return sum(open, "total"); }
const week = sum(sessions, "productive"); const team = await Org.active(); export const live = stream(team, week, { realtime: true });
// the studio runs entirely on this — one workspace for time, projects, clients, shifts, payroll and the client portal
// timeros — sessions are classified on the device, then rolled up into projects, payroll and invoices
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core"; import { classify } from "./on-device/model";
export async function track(user: User): Promise<Session> { const session = await Timer.start(user.id, { mode: "auto" });
session.on("activity", (event) => { const kind = classify(event); session.record(kind, event.duration); }); // classification stays on the machine
session.on("idle", () => session.pause()); return session.commit({ at: trustedNow(), tz: user.timezone, by: user.id });
}
// roll the week up into projects: cost comes from payroll, revenue from the client rate, profit falls out of the two
export function settle(projects: Project[]): Money[] { const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR", period: "month" }); } // run once a month
export async function invoice(client: Client) { const open = await Invoice.pending(client.id); return sum(open, "total"); }
const week = sum(sessions, "productive"); const team = await Org.active(); export const live = stream(team, week, { realtime: true });
// the studio runs entirely on this — one workspace for time, projects, clients, shifts, payroll and the client portal
// timeros — sessions are classified on the device, then rolled up into projects, payroll and invoices
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core"; import { classify } from "./on-device/model";
export async function track(user: User): Promise<Session> { const session = await Timer.start(user.id, { mode: "auto" });
session.on("activity", (event) => { const kind = classify(event); session.record(kind, event.duration); }); // classification stays on the machine
session.on("idle", () => session.pause()); return session.commit({ at: trustedNow(), tz: user.timezone, by: user.id });
}
// roll the week up into projects: cost comes from payroll, revenue from the client rate, profit falls out of the two
export function settle(projects: Project[]): Money[] { const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR", period: "month" }); } // run once a month
export async function invoice(client: Client) { const open = await Invoice.pending(client.id); return sum(open, "total"); }
const week = sum(sessions, "productive"); const team = await Org.active(); export const live = stream(team, week, { realtime: true });
// the studio runs entirely on this — one workspace for time, projects, clients, shifts, payroll and the client portal
// timeros — sessions are classified on the device, then rolled up into projects, payroll and invoices
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core"; import { classify } from "./on-device/model";
export async function track(user: User): Promise<Session> { const session = await Timer.start(user.id, { mode: "auto" });
session.on("activity", (event) => { const kind = classify(event); session.record(kind, event.duration); }); // classification stays on the machine
session.on("idle", () => session.pause()); return session.commit({ at: trustedNow(), tz: user.timezone, by: user.id });
}
// roll the week up into projects: cost comes from payroll, revenue from the client rate, profit falls out of the two
export function settle(projects: Project[]): Money[] { const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR", period: "month" }); } // run once a month
export async function invoice(client: Client) { const open = await Invoice.pending(client.id); return sum(open, "total"); }
const week = sum(sessions, "productive"); const team = await Org.active(); export const live = stream(team, week, { realtime: true });
// the studio runs entirely on this — one workspace for time, projects, clients, shifts, payroll and the client portal
// timeros — sessions are classified on the device, then rolled up into projects, payroll and invoices
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core"; import { classify } from "./on-device/model";
export async function track(user: User): Promise<Session> { const session = await Timer.start(user.id, { mode: "auto" });
session.on("activity", (event) => { const kind = classify(event); session.record(kind, event.duration); }); // classification stays on the machine
session.on("idle", () => session.pause()); return session.commit({ at: trustedNow(), tz: user.timezone, by: user.id });
}
// roll the week up into projects: cost comes from payroll, revenue from the client rate, profit falls out of the two
export function settle(projects: Project[]): Money[] { const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR", period: "month" }); } // run once a month
export async function invoice(client: Client) { const open = await Invoice.pending(client.id); return sum(open, "total"); }
const week = sum(sessions, "productive"); const team = await Org.active(); export const live = stream(team, week, { realtime: true });
// the studio runs entirely on this — one workspace for time, projects, clients, shifts, payroll and the client portal
// timeros — sessions are classified on the device, then rolled up into projects, payroll and invoices
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core"; import { classify } from "./on-device/model";
export async function track(user: User): Promise<Session> { const session = await Timer.start(user.id, { mode: "auto" });
session.on("activity", (event) => { const kind = classify(event); session.record(kind, event.duration); }); // classification stays on the machine
session.on("idle", () => session.pause()); return session.commit({ at: trustedNow(), tz: user.timezone, by: user.id });
}
// roll the week up into projects: cost comes from payroll, revenue from the client rate, profit falls out of the two
export function settle(projects: Project[]): Money[] { const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR", period: "month" }); } // run once a month
export async function invoice(client: Client) { const open = await Invoice.pending(client.id); return sum(open, "total"); }
const week = sum(sessions, "productive"); const team = await Org.active(); export const live = stream(team, week, { realtime: true });
// the studio runs entirely on this — one workspace for time, projects, clients, shifts, payroll and the client portal
// timeros — sessions are classified on the device, then rolled up into projects, payroll and invoices
import { Timer, Session, Project, Invoice, Client } from "@vezoft/core"; import { classify } from "./on-device/model";
export async function track(user: User): Promise<Session> { const session = await Timer.start(user.id, { mode: "auto" });
session.on("activity", (event) => { const kind = classify(event); session.record(kind, event.duration); }); // classification stays on the machine
session.on("idle", () => session.pause()); return session.commit({ at: trustedNow(), tz: user.timezone, by: user.id });
}
// roll the week up into projects: cost comes from payroll, revenue from the client rate, profit falls out of the two
export function settle(projects: Project[]): Money[] { const profit = projects.map((p) => p.hours * p.rate - p.cost);
return payroll.run(profit, { region: "EU", currency: "EUR", period: "month" }); } // run once a month
export async function invoice(client: Client) { const open = await Invoice.pending(client.id); return sum(open, "total"); }
const week = sum(sessions, "productive"); const team = await Org.active(); export const live = stream(team, week, { realtime: true });
// the studio runs entirely on this — one workspace for time, projects, clients, shifts, payroll and the client portal