feat(dashboard): add transactions and users management pages with CRUD functionality
feat(auth): implement login page and authentication middleware feat(sidebar): create sidebar component with user info and navigation links feat(api): establish API client for interacting with backend services
This commit is contained in:
85
apps/csms/src/ocpp/actions/start-transaction.ts
Normal file
85
apps/csms/src/ocpp/actions/start-transaction.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { useDrizzle } from "@/lib/db.js";
|
||||
import { chargePoint, connector, transaction } from "@/db/schema.js";
|
||||
import type {
|
||||
StartTransactionRequest,
|
||||
StartTransactionResponse,
|
||||
OcppConnectionContext,
|
||||
} from "../types.ts";
|
||||
import { resolveIdTagInfo } from "./authorize.ts";
|
||||
|
||||
export async function handleStartTransaction(
|
||||
payload: StartTransactionRequest,
|
||||
ctx: OcppConnectionContext,
|
||||
): Promise<StartTransactionResponse> {
|
||||
const db = useDrizzle();
|
||||
|
||||
// Resolve idTag authorization
|
||||
const idTagInfo = await resolveIdTagInfo(payload.idTag);
|
||||
|
||||
// Find charge point
|
||||
const [cp] = await db
|
||||
.select({ id: chargePoint.id })
|
||||
.from(chargePoint)
|
||||
.where(eq(chargePoint.chargePointIdentifier, ctx.chargePointIdentifier))
|
||||
.limit(1);
|
||||
|
||||
if (!cp) throw new Error(`ChargePoint not found: ${ctx.chargePointIdentifier}`);
|
||||
|
||||
// Upsert connector — it may not exist yet if StatusNotification was skipped
|
||||
const [conn] = await db
|
||||
.insert(connector)
|
||||
.values({
|
||||
id: crypto.randomUUID(),
|
||||
chargePointId: cp.id,
|
||||
connectorId: payload.connectorId,
|
||||
status: "Charging",
|
||||
errorCode: "NoError",
|
||||
lastStatusAt: new Date(),
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: [connector.chargePointId, connector.connectorId],
|
||||
set: { status: "Charging", updatedAt: new Date() },
|
||||
})
|
||||
.returning({ id: connector.id });
|
||||
|
||||
const rejected = idTagInfo.status !== "Accepted";
|
||||
const now = new Date();
|
||||
|
||||
// Insert transaction record regardless of auth status (OCPP spec requirement)
|
||||
const [tx] = await db
|
||||
.insert(transaction)
|
||||
.values({
|
||||
chargePointId: cp.id,
|
||||
connectorId: conn.id,
|
||||
connectorNumber: payload.connectorId,
|
||||
idTag: payload.idTag,
|
||||
idTagStatus: idTagInfo.status,
|
||||
startTimestamp: new Date(payload.timestamp),
|
||||
startMeterValue: payload.meterStart,
|
||||
reservationId: payload.reservationId ?? null,
|
||||
// If rejected, immediately close the transaction so it doesn't appear as in-progress
|
||||
...(rejected && {
|
||||
stopTimestamp: now,
|
||||
stopMeterValue: payload.meterStart,
|
||||
chargeAmount: 0,
|
||||
stopReason: "DeAuthorized",
|
||||
}),
|
||||
})
|
||||
.returning({ id: transaction.id });
|
||||
|
||||
// If rejected, reset connector back to Available
|
||||
if (rejected) {
|
||||
await db
|
||||
.update(connector)
|
||||
.set({ status: "Available", updatedAt: now })
|
||||
.where(eq(connector.id, conn.id));
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[OCPP] StartTransaction cp=${ctx.chargePointIdentifier} connector=${payload.connectorId} ` +
|
||||
`idTag=${payload.idTag} status=${idTagInfo.status} txId=${tx.id}`,
|
||||
);
|
||||
|
||||
return { transactionId: tx.id, idTagInfo };
|
||||
}
|
||||
Reference in New Issue
Block a user