feat(lstv2 move): moved lstv2 into this app to keep them combined and easier to maintain

This commit is contained in:
2025-09-19 22:22:05 -05:00
parent caf2315191
commit e4477402ad
847 changed files with 165801 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
import { OpenAPIHono } from "@hono/zod-openapi";
import login from "./routes/login.js";
import register from "./routes/register.js";
import session from "./routes/session.js";
import getAccess from "./routes/user/getUserRoles.js";
import setAccess from "./routes/userAdmin/setUserRoles.js";
import profile from "./routes/user/profileUpdate.js";
import { areRolesIn } from "./utils/roleCheck.js";
import createUser from "./routes/userAdmin/createUser.js";
import allUsers from "./routes/userAdmin/getUsers.js";
import updateUser from "./routes/userAdmin/updateUser.js";
import allUserRoles from "./routes/userAdmin/getAllUserRoles.js";
import { massAccountCreation } from "./utils/DefaultAccountCreation.js";
const app = new OpenAPIHono();
// run the role check
setTimeout(() => {
areRolesIn();
}, 5000);
const routes = [
login,
register,
session,
profile,
getAccess,
setAccess,
createUser,
allUsers,
allUserRoles,
updateUser,
] as const;
// app.route("/server", modules);
const appRoutes = routes.forEach((route) => {
app.route("/auth", route);
});
// setTimeout(() => {
// massAccountCreation();
// }, 1000 * 60);
export default app;

View File

@@ -0,0 +1,70 @@
import jwt from "jsonwebtoken";
import {db} from "../../../../database/dbclient.js";
import {users} from "../../../../database/schema/users.js";
import {eq, sql} from "drizzle-orm";
import {checkPassword} from "../utils/checkPassword.js";
import {roleCheck} from "./userRoles/getUserAccess.js";
import {createLog} from "../../logger/logger.js";
import {differenceInDays} from "date-fns";
/**
* Authenticate a user and return a JWT.
*/
const {sign} = jwt;
export async function login(
username: string,
password: string
): Promise<{token: string; user: {user_id: string; username: string}}> {
const user = await db.select().from(users).where(eq(users.username, username));
//console.log(user);
if (user.length === 0) {
throw new Error("Invalid or Missing user");
}
// check the password
const checkedPass = await checkPassword(password, user[0]?.password);
//console.log(checkedPass);
if (!checkedPass) {
throw new Error("Invalid Password");
}
// Create a JWT
const secret: string = process.env.JWT_SECRET!;
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
// get the user roles
const roles = await roleCheck(user[0].user_id);
const userData = {
user_id: user[0].user_id,
username: user[0].username,
email: user[0].email,
//roles: roles || null,
role: user[0].role || null, // this should be removed onces full migration to v2 is completed
prod: btoa(`${username.toLowerCase()}:${password}`),
};
// update the user last login
try {
const lastLog = await db
.update(users)
.set({lastLogin: sql`NOW()`})
.where(eq(users.user_id, user[0].user_id))
.returning({lastLogin: users.lastLogin});
createLog(
"info",
"lst",
"auth",
`Its been ${differenceInDays(lastLog[0]?.lastLogin ?? "", new Date(Date.now()))} days since ${
user[0].username
} has logged in`
);
//]);
} catch (error) {
createLog("error", "lst", "auth", "There was an error updating the user last login");
}
const token = sign({user: userData}, secret, {expiresIn: expiresIn * 60});
return {token, user: userData};
}

View File

@@ -0,0 +1,8 @@
/**
* Logout (clear the token).
* This is a placeholder function since JWTs are stateless.
* In a real app, you might want to implement token blacklisting.
*/
export function logout(): {message: string} {
return {message: "Logout successful"};
}

View File

@@ -0,0 +1,62 @@
import { eq } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { users } from "../../../../database/schema/users.js";
import { createPassword } from "../utils/createPassword.js";
import { setSysAdmin } from "./userRoles/setSysAdmin.js";
import { createLog } from "../../logger/logger.js";
export const registerUser = async (
username: string,
password: string,
email: string
) => {
const usercount = await db.select().from(users);
// make sure the user dose not already exist in the system
const userCheck = await db
.select()
.from(users)
.where(eq(users.username, username));
if (userCheck.length === 1) {
return {
success: false,
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
};
}
// make sure we only send over a username that is all lowercase
username = username.toLowerCase();
// get the good kinda password
password = await createPassword(password);
try {
const user = await db
.insert(users)
.values({ username, email, password })
.returning({ user: users.username, email: users.email });
if (usercount.length === 0) {
createLog(
"info",
"auth",
"auth",
`${username} is the first user and will be set to system admin.`
);
const updateUser = await db
.select()
.from(users)
.where(eq(users.username, username));
setSysAdmin(updateUser, "systemAdmin");
}
return { success: true, message: "User Registered", user };
} catch (error) {
createLog("error", "auth", "auth", `${error}`);
return {
success: false,
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
};
}
};

View File

@@ -0,0 +1,26 @@
import { db } from "../../../../../database/dbclient.js";
import { userRoles } from "../../../../../database/schema/userRoles.js";
import { returnRes } from "../../../../globalUtils/routeDefs/returnRes.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { createLog } from "../../../logger/logger.js";
export const getAllUsersRoles = async () => {
/**
* returns all users that are in lst
*/
createLog("info", "apiAuthedRoute", "auth", "Get all users");
const { data, error } = await tryCatch(db.select().from(userRoles));
if (error) {
return returnRes(
false,
"auth",
"auth",
"There was an error getting users",
"error",
new Error("No user exists.")
);
}
return returnRes(true, "auth", "auth", "All users.", "info", data);
};

View File

@@ -0,0 +1,45 @@
import { db } from "../../../../../database/dbclient.js";
import { userRoles } from "../../../../../database/schema/userRoles.js";
import { users } from "../../../../../database/schema/users.js";
import { returnRes } from "../../../../globalUtils/routeDefs/returnRes.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { createLog } from "../../../logger/logger.js";
export const getAllUsers = async () => {
/**
* returns all users that are in lst
*/
createLog("info", "apiAuthedRoute", "auth", "Get all users");
// get all users
const { data, error } = await tryCatch(db.select().from(users));
// add there modules they are in
const { data: m, error: em } = await tryCatch(db.select().from(userRoles));
const user: any = data;
const userData = user.map((i: any) => {
// module in
const module = m?.filter((x: any) => x.user_id === i.user_id);
if (module) {
return { ...i, moduleRoles: module };
} else {
return;
}
});
if (error) {
returnRes(
false,
"auth",
"auth",
"There was an error getting users",
"error",
new Error("No user exists.")
);
}
//returnRes(true, "All users.", data);
return { success: true, message: "All users", data: userData };
};

View File

@@ -0,0 +1,102 @@
import { eq } from "drizzle-orm";
import { db } from "../../../../../database/dbclient.js";
import { users } from "../../../../../database/schema/users.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import type { User } from "../../../../types/users.js";
import { createPassword } from "../../utils/createPassword.js";
import { createLog } from "../../../logger/logger.js";
import { sendEmail } from "../../../notifications/controller/sendMail.js";
import { settings } from "../../../../../database/schema/settings.js";
import { getSettings } from "../../../server/controller/settings/getSettings.js";
export const updateUserADM = async (userData: User) => {
/**
* The user model will need to be passed over so we can update per the request on the user.
* password, username, email.
*/
createLog(
"info",
"apiAuthedRoute",
"auth",
`${userData.user_id} is being updated.`
);
// get the orignal user info
const { data: user, error: userError } = await tryCatch(
db.select().from(users).where(eq(users.user_id, userData.user_id!))
);
if (userError) {
return {
success: false,
message: "There was an error getting the user",
userError,
};
}
if (user?.length === 0) {
return {
success: false,
message:
"The user you are looking for has either been deleted or dose not exist.",
};
}
//const { data: s, error: se } = await tryCatch(db.select().from(settings));
const { data: s, error: se } = await tryCatch(getSettings());
if (se) {
return {
success: false,
message: `There was an error getting setting data to post to the server.`,
data: se,
};
}
const set: any = s;
const server = set.filter((n: any) => n.name === "server");
const port = set.filter((n: any) => n.name === "serverPort");
const upd_user = user as User;
const password: string = userData.password
? await createPassword(userData.password!)
: upd_user.password!;
const data = {
username: userData.username ? userData.username : upd_user?.username,
password: password,
email: userData.email ? userData.email : upd_user.email,
role: userData.role ? userData.role : upd_user.role,
};
// term ? ilike(posts.title, term) : undefined
const { data: updData, error: updError } = await tryCatch(
db.update(users).set(data).where(eq(users.user_id, userData.user_id!))
);
if (updError) {
return {
success: false,
message: "There was an error getting the user",
updError,
};
}
if (userData?.password!.length > 0) {
// send this user an email so they have the randomized password.
await sendEmail({
email: user[0]?.email,
subject: "LST - Password reset.",
template: "passwordReset",
context: {
password: userData.password!,
username: user[0].username!,
server: server[0].value,
port: port[0].value,
},
});
}
return {
success: true,
message: `${userData.username} has been updated.`,
updData,
};
};

View File

@@ -0,0 +1,41 @@
/*
pass over a users uuid and return all modules they have permission too.
in the login route we attach it to user under roles.
*/
import { eq } from "drizzle-orm";
import { db } from "../../../../../database/dbclient.js";
import { userRoles } from "../../../../../database/schema/userRoles.js";
export const roleCheck = async (user_id: string | undefined) => {
if (!user_id) {
throw Error("Missing user_id");
}
let returnRoles: any = [];
// get the user roles by the user_id
returnRoles = await db
.select({
user_id: userRoles.user_id,
role_id: userRoles.role_id,
module_id: userRoles.module_id,
role: userRoles.role,
})
.from(userRoles)
.where(eq(userRoles.user_id, user_id));
if (returnRoles[0]?.role.includes("systemAdmin")) {
const roles = await db
.select({
user_id: userRoles.user_id,
role_id: userRoles.role_id,
module_id: userRoles.module_id,
role: userRoles.role,
})
.from(userRoles)
.where(eq(userRoles.user_id, user_id));
return roles;
}
return returnRoles;
};

View File

@@ -0,0 +1,41 @@
import {users} from "../../../../../database/schema/users.js";
import {eq} from "drizzle-orm";
import {db} from "../../../../../database/dbclient.js";
import {userRoles} from "../../../../../database/schema/userRoles.js";
import {modules} from "../../../../../database/schema/modules.js";
import {roles} from "../../../../../database/schema/roles.js";
import {createLog} from "../../../logger/logger.js";
export const setSysAdmin = async (user: any, roleName: any): Promise<void> => {
// remove all userRoles to prevent errors
try {
const remove = await db.delete(userRoles).where(eq(userRoles.user_id, user[0].user_id));
} catch (error) {
console.log(error);
}
// now we want to add the user to the system admin.
const module = await db.select().from(modules);
const role = await db.select().from(roles).where(eq(roles.name, roleName));
for (let i = 0; i < module.length; i++) {
try {
const userRole = await db.insert(userRoles).values({
user_id: user[0].user_id,
role_id: role[0].role_id,
module_id: module[i].module_id,
role: roleName,
});
createLog(
"info",
user[0].username,
"auth",
`${user[0].username} has been granted access to ${module[i].name} with the role ${roleName}`
);
} catch (error) {
createLog("info", "lst", "auth", `Error settings user access: ${error}`);
}
}
return;
};

View File

@@ -0,0 +1,111 @@
/*
pass over a users uuid and return all modules they have permission too.
in the login route we attach it to user under roles.
*/
import { and, eq } from "drizzle-orm";
import { db } from "../../../../../database/dbclient.js";
import { userRoles } from "../../../../../database/schema/userRoles.js";
import { users } from "../../../../../database/schema/users.js";
import { modules } from "../../../../../database/schema/modules.js";
import { roles } from "../../../../../database/schema/roles.js";
import { setSysAdmin } from "./setSysAdmin.js";
export const setUserAccess = async (
username: string,
moduleName: string,
roleName: string,
override?: string
) => {
// get the user roles by the user_id
const user = await db
.select()
.from(users)
.where(eq(users.username, username));
const module = await db
.select()
.from(modules)
.where(eq(modules.name, moduleName));
if (
process.env.SECRETOVERRIDECODE != override &&
roleName === "systemAdmin"
) {
return {
success: false,
message: "The override code provided is invalid.",
};
}
const role = await db.select().from(roles).where(eq(roles.name, roleName));
/**
* For system admin we want to do a little more
*/
if (roleName === "systemAdmin") {
await setSysAdmin(user, roleName);
return {
success: true,
message: `${username} has been granted access to ${moduleName} with the role ${roleName}`,
};
}
//console.log(user, module, role);
// set the user
try {
const userRole = await db
.insert(userRoles)
.values({
user_id: user[0].user_id,
role_id: role[0].role_id,
module_id: module[0].module_id,
role: roleName,
})
.onConflictDoUpdate({
target: userRoles.user_id,
set: { role_id: role[0].role_id, role: roleName },
});
//.returning({user: users.username, email: users.email});
// return c.json({message: "User Registered", user}, 200);
return {
success: true,
message: `${username} has been granted access to ${moduleName} with the role ${roleName}`,
};
} catch (error) {
await changeRole(
roleName,
user[0].user_id,
module[0].module_id,
role[0].role_id
);
return {
success: true,
message: `${username} access on ${moduleName} has been changed to ${roleName}`,
};
}
};
const changeRole = async (
role: any,
userID: any,
moduleID: any,
roleID: any
) => {
await db
.delete(userRoles)
.where(
and(
eq(userRoles.user_id, userID),
eq(userRoles.module_id, moduleID)
)
);
const userRole = await db.insert(userRoles).values({
user_id: userID,
role_id: roleID,
module_id: moduleID,
role: role,
});
};

View File

@@ -0,0 +1,42 @@
import {eq, sql} from "drizzle-orm";
import {db} from "../../../../../database/dbclient.js";
import {users} from "../../../../../database/schema/users.js";
import {createLog} from "../../../logger/logger.js";
import {createPassword} from "../../utils/createPassword.js";
const blacklistedTokens = new Set();
function blacklistToken(token: string) {
blacklistedTokens.add(token);
setTimeout(() => blacklistedTokens.delete(token), 3600 * 1000); // Remove after 1 hour
}
function isTokenBlacklisted(token: string) {
return blacklistedTokens.has(token);
}
export const updateProfile = async (user: any, data: any, token: string) => {
if (isTokenBlacklisted(token)) {
createLog("warn", user.username, "auth", `${user.username} is trying to use a black listed token`);
throw Error("This token was already used");
}
//re salt and encrypt the password
try {
const saltPass = await createPassword(data.password);
// update the password
const profileUpdate = await db
.update(users)
.set({password: saltPass, upd_user: user.username, upd_date: sql`NOW()`})
.where(eq(users.user_id, user.user_id));
blacklistToken(token);
} catch (error) {
createLog(
"error",
user.username,
"auth",
`Error: ${JSON.stringify(error)}, "There was an error updating the users profile"`
);
}
};

View File

@@ -0,0 +1,17 @@
import {sign, verify} from "jsonwebtoken";
/**
* Verify a JWT and return the decoded payload.
*/
const secret: string = process.env.JWT_SECRET! || "bnghsjhsd";
const expiresIn: string = process.env.JWT_EXPIRES! || "1h";
export function verifyToken(token: string): {userId: number} {
try {
const payload = verify(token, secret) as {userId: number};
return payload;
} catch (err) {
throw new Error("Invalid token");
}
}

View File

@@ -0,0 +1,45 @@
import {type MiddlewareHandler} from "hono";
import jwt from "jsonwebtoken";
const {sign, verify} = jwt;
export const authMiddleware: MiddlewareHandler = async (c, next) => {
const authHeader = c.req.header("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return c.json({error: "Unauthorized"}, 401);
}
const token = authHeader.split(" ")[1];
try {
const decoded = verify(token, process.env.JWT_SECRET!, {ignoreExpiration: false}) as {
userId: number;
exp: number;
};
const currentTime = Math.floor(Date.now() / 1000); // Get current timestamp
const timeLeft = decoded.exp - currentTime;
// If the token has less than REFRESH_THRESHOLD seconds left, refresh it
let newToken = null;
if (timeLeft < parseInt(process.env.REFRESH_THRESHOLD!)) {
newToken = sign({userId: decoded.userId}, process.env.JWT_SECRET!, {
expiresIn: parseInt(process.env.EXPIRATION_TIME!),
});
c.res.headers.set("Authorization", `Bearer ${newToken}`);
}
c.set("user", {id: decoded.userId});
await next();
// If a new token was generated, send it in response headers
if (newToken) {
console.log("token was refreshed");
c.res.headers.set("X-Refreshed-Token", newToken);
}
} catch (err) {
return c.json({error: "Invalid token"}, 401);
}
};

View File

@@ -0,0 +1,85 @@
import { createMiddleware } from "hono/factory";
import type { CustomJwtPayload } from "../../../types/jwtToken.js";
import { verify } from "hono/jwt";
import { db } from "../../../../database/dbclient.js";
import { modules } from "../../../../database/schema/modules.js";
import { and, eq } from "drizzle-orm";
import { userRoles } from "../../../../database/schema/userRoles.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
const hasCorrectRole = (requiredRole: string[], module: string) =>
createMiddleware(async (c, next) => {
/**
* We want to check to make sure you have the correct role to be here
*/
const authHeader = c.req.header("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return c.json({ error: "Unauthorized" }, 401);
}
const token = authHeader.split(" ")[1];
// deal with token data
const { data: tokenData, error: tokenError } = await tryCatch(
verify(token, process.env.JWT_SECRET!)
);
if (tokenError) {
return c.json({ error: "Invalid token" }, 401);
}
const customToken = tokenData as CustomJwtPayload;
// Get the module
const { data: mod, error: modError } = await tryCatch(
db.select().from(modules).where(eq(modules.name, module))
);
if (modError) {
console.log(modError);
return;
}
if (mod.length === 0) {
return c.json({ error: "You have entered an invalid module name" }, 403);
}
// check if the user has the role needed to get into this module
const { data: userRole, error: userRoleError } = await tryCatch(
db
.select()
.from(userRoles)
.where(
and(
eq(userRoles.module_id, mod[0].module_id),
eq(userRoles.user_id, customToken.user?.user_id!)
)
)
);
if (userRoleError) {
return;
}
if (!userRole) {
return c.json(
{
error:
"The module you are trying to access is not active or is invalid.",
},
403
);
}
if (!requiredRole.includes(userRole[0]?.role)) {
return c.json(
{ error: "You do not have access to this part of the app." },
403
);
}
await next();
});
export default hasCorrectRole;

View File

@@ -0,0 +1,97 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { login } from "../controllers/login.js";
const app = new OpenAPIHono();
const UserSchema = z
.object({
username: z.string().optional().openapi({ example: "smith002" }),
//email: z.string().optional().openapi({example: "s.smith@example.com"}),
password: z.string().openapi({ example: "password123" }),
})
.openapi("User");
const route = createRoute({
tags: ["Auth"],
summary: "Login as user",
description: "Login as a user to get a JWT token",
method: "post",
path: "/login",
request: {
body: {
content: {
"application/json": { schema: UserSchema },
},
},
},
responses: {
200: {
content: {
"application/json": {
schema: z.object({
success: z.boolean().openapi({ example: true }),
message: z.string().openapi({ example: "Logged in" }),
}),
},
},
description: "Response message",
},
400: {
content: {
"application/json": {
schema: z.object({
success: z.boolean().openapi({ example: false }),
message: z
.string()
.openapi({ example: "Username and password required" }),
}),
},
},
description: "Bad request",
},
401: {
content: {
"application/json": {
schema: z.object({
success: z.boolean().openapi({ example: false }),
message: z
.string()
.openapi({ example: "Username and password required" }),
}),
},
},
description: "Bad request",
},
},
});
app.openapi(route, async (c) => {
const { username, password, email } = await c.req.json();
if (!username || !password) {
return c.json(
{
success: false,
message: "Username and password are required",
},
400
);
}
try {
const { token, user } = await login(username.toLowerCase(), password);
// Set the JWT as an HTTP-only cookie
//c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`);
return c.json(
{ success: true, message: "Login successful", user, token },
200
);
} catch (err) {
return c.json({ success: false, message: "Incorrect Credentials" }, 401);
}
});
export default app;

View File

@@ -0,0 +1,110 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { registerUser } from "../controllers/register.js";
const app = new OpenAPIHono();
const UserSchema = z.object({
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.openapi({ example: "smith034" }),
email: z.string().email().openapi({ example: "smith@example.com" }),
password: z
.string()
.min(6, { message: "Passwords must be longer than 3 characters" })
.regex(/[A-Z]/, {
message: "Password must contain at least one uppercase letter",
})
.regex(/[\W_]/, {
message: "Password must contain at least one special character",
})
.openapi({ example: "Password1!" }),
});
type User = z.infer<typeof UserSchema>;
const responseSchema = z.object({
success: z.boolean().optional().openapi({ example: true }),
message: z.string().optional().openapi({ example: "User Created" }),
});
app.openapi(
createRoute({
tags: ["Auth"],
summary: "Register a new user",
method: "post",
path: "/register",
request: {
body: {
content: {
"application/json": { schema: UserSchema },
},
},
},
responses: {
200: {
content: { "application/json": { schema: responseSchema } },
description: "Retrieve the user",
},
400: {
content: {
"application/json": {
schema: z.object({
success: z.boolean().openapi({ example: false }),
message: z
.string()
.openapi({ example: "Invalid credentials passed" }),
}),
},
},
description: "Retrieve the user",
},
},
}),
async (c) => {
// apit hit
apiHit(c, { endpoint: "api/auth/register" });
let { username, email, password } = await c.req.json();
if (!username || !email || !password) {
return c.json({ success: false, message: "Credentials missing" }, 400);
}
// some usernames that should be ignored
const badActors = ["admin", "root"];
if (badActors.includes(username)) {
return c.json(
{
success: false,
message: `${username} is not a valid name to be registerd please try again`,
},
400
);
}
try {
const register = await registerUser(username, password, email);
return c.json(
{
success: register.success,
message: register.message,
user: register?.user,
},
200
);
} catch (error) {
console.log(error);
return c.json(
{
success: false,
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,122 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { verify } from "hono/jwt";
import { authMiddleware } from "../middleware/authMiddleware.js";
import jwt from "jsonwebtoken";
const session = new OpenAPIHono();
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
const secret: string = process.env.JWT_SECRET!;
const { sign } = jwt;
const UserSchema = z.object({
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.openapi({ example: "smith034" }),
email: z.string().email().openapi({ example: "smith@example.com" }),
password: z
.string()
.min(6, { message: "Passwords must be longer than 3 characters" })
.regex(/[A-Z]/, {
message: "Password must contain at least one uppercase letter",
})
.regex(/[\W_]/, {
message: "Password must contain at least one special character",
})
.openapi({ example: "Password1!" }),
});
session.openapi(
createRoute({
tags: ["Auth"],
summary: "Checks a user session based on there token",
description: "Can post there via Authentiaction header or cookies",
method: "get",
path: "/session",
middleware: authMiddleware,
// request: {
// body: {
// content: {
// "application/json": {schema: UserSchema},
// },
// },
// },
responses: {
200: {
content: {
"application/json": {
schema: z.object({
data: z.object({
token: z
.string()
.openapi({
example: "sdkjhgsldkvhdakl;jvhs;adkjfhvds.kvnsad;ovhads",
}),
// user: z.object({
// user_id: z.string().openapi({example: "04316c86-f086-4cc6-b3d4-cca164a26f3f"}),
// username: z.string().openapi({example: "smith"}),
// email: z.string().openapi({example: "smith@example.com"}).optional(),
// }),
}),
}),
},
},
description: "Login successful",
},
401: {
content: {
"application/json": {
schema: z.object({
message: z.string().openapi({ example: "Unathenticated" }),
}),
},
},
description: "Error of why you were not logged in.",
},
},
}),
async (c) => {
const authHeader = c.req.header("Authorization");
if (authHeader?.includes("Basic")) {
return c.json(
{ message: "You are a Basic user! Please login to get a token" },
401
);
}
if (!authHeader) {
return c.json({ message: "Unauthorized" }, 401);
}
const token = authHeader?.split("Bearer ")[1] || "";
try {
const payload = await verify(token, process.env.JWT_SECRET!);
// If it's valid, return a new token
const newToken = sign({ user: payload.user }, secret, {
expiresIn: expiresIn * 60,
});
return c.json({ data: { token: newToken, user: payload.user } }, 200);
} catch (error) {
return c.json({ message: "Unauthorized" }, 401);
}
}
);
// const token = authHeader?.split("Bearer ")[1] || "";
// try {
// const payload = await verify(token, process.env.JWT_SECRET!);
// //console.log(payload);
// //return c.json({data: {token, user: payload.user}}, 200);
// return c.json({message: "something"});
// } catch (err) {
// return c.json({error: "Invalid or expired token"}, 401);
// }
// });
export default session;

View File

@@ -0,0 +1,59 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
import jwt from "jsonwebtoken";
import type { CustomJwtPayload } from "../../../../types/jwtToken.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import { roleCheck } from "../../controllers/userRoles/getUserAccess.js";
const { verify } = jwt;
const app = new OpenAPIHono();
const responseSchema = z.object({
message: z.string().optional().openapi({ example: "User Created" }),
});
app.openapi(
createRoute({
tags: ["auth:user"],
summary: "returns the users access",
method: "get",
path: "/getuseraccess",
middleware: [authMiddleware],
responses: {
200: {
content: { "application/json": { schema: responseSchema } },
description: "Retrieve the user",
},
},
}),
async (c) => {
// apit hit
//apiHit(c, { endpoint: "api/auth/getUserRoles" });
const authHeader = c.req.header("Authorization");
const token = authHeader?.split("Bearer ")[1] || "";
try {
const secret = process.env.JWT_SECRET!;
if (!secret) {
throw new Error("JWT_SECRET is not defined in environment variables");
}
const payload = verify(token, secret) as CustomJwtPayload;
const canAccess = await roleCheck(payload.user?.user_id);
return c.json(
{
sucess: true,
message: `User ${payload.user?.username} can access`,
data: canAccess,
},
200
);
} catch (error) {
console.log(error);
}
return c.json({ message: "UserRoles coming over" });
}
);
export default app;

View File

@@ -0,0 +1,130 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import { updateProfile } from "../../controllers/users/updateProfile.js";
import { verify } from "hono/jwt";
import { createLog } from "../../../logger/logger.js";
const app = new OpenAPIHono();
const UserSchema = z.object({
password: z
.string()
.min(6, { message: "Passwords must be longer than 3 characters" })
.regex(/[A-Z]/, {
message: "Password must contain at least one uppercase letter",
})
.regex(/[\W_]/, {
message: "Password must contain at least one special character",
})
.openapi({ example: "Password1!" }),
});
app.openapi(
createRoute({
tags: ["auth:user"],
summary: "Updates a users Profile",
description: "Currently you can only update your password over the API",
method: "patch",
path: "/profile",
middleware: authMiddleware,
request: {
body: {
content: {
"application/json": { schema: UserSchema },
},
},
},
responses: {
200: {
content: {
"application/json": {
schema: z.object({
message: z.string().optional().openapi({
example: "User Profile has been updated",
}),
}),
},
},
description: "Sucess return",
},
401: {
content: {
"application/json": {
schema: z.object({
message: z
.string()
.optional()
.openapi({ example: "Unauthenticated" }),
}),
},
},
description: "Unauthorized",
},
500: {
content: {
"application/json": {
schema: z.object({
message: z
.string()
.optional()
.openapi({ example: "Internal Server error" }),
}),
},
},
description: "Internal Server Error",
},
},
}),
async (c) => {
// make sure we have a vaid user being accessed thats really logged in
const authHeader = c.req.header("Authorization");
if (authHeader?.includes("Basic")) {
return c.json(
{
message:
"You are a Basic user! Please login to get a token",
},
401
);
}
if (!authHeader) {
return c.json({ success: false, message: "Unauthorized" }, 401);
}
const token = authHeader?.split("Bearer ")[1] || "";
let user;
try {
const payload = await verify(token, process.env.JWT_SECRET!);
user = payload.user;
} catch (error) {
createLog(
"error",
"lst",
"auth",
"Failed session check, user must be logged out"
);
return c.json({ success: false, message: "Unauthorized" }, 401);
}
// now pass all the data over to update the user info
try {
const data = await c?.req.json();
await updateProfile(user, data, token);
return c.json({
success: true,
message: "Your profile has been updated",
});
} catch (error) {
console.log(error);
return c.json({
success: false,
message: "There was an error",
error,
});
}
}
);
export default app;

View File

@@ -0,0 +1,115 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { registerUser } from "../../controllers/register.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import hasCorrectRole from "../../middleware/roleCheck.js";
const app = new OpenAPIHono();
const UserSchema = z.object({
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.openapi({ example: "smith034" }),
email: z.string().email().openapi({ example: "smith@example.com" }),
password: z
.string()
.min(6, { message: "Passwords must be longer than 3 characters" })
.regex(/[A-Z]/, {
message: "Password must contain at least one uppercase letter",
})
.regex(/[\W_]/, {
message: "Password must contain at least one special character",
})
.openapi({ example: "Password1!" }),
});
type User = z.infer<typeof UserSchema>;
const responseSchema = z.object({
success: z.boolean().optional().openapi({ example: true }),
message: z.string().optional().openapi({ example: "User Created" }),
});
app.openapi(
createRoute({
tags: ["Auth:admin"],
summary: "Creates user",
method: "post",
path: "/",
middleware: [
authMiddleware,
hasCorrectRole(["admin", "systemAdmin"], "admin"),
],
request: {
body: {
content: {
"application/json": { schema: UserSchema },
},
},
},
responses: {
200: {
content: { "application/json": { schema: responseSchema } },
description: "Retrieve the user",
},
400: {
content: {
"application/json": {
schema: z.object({
success: z.boolean().openapi({ example: false }),
message: z
.string()
.openapi({ example: "Invalid credentials passed" }),
}),
},
},
description: "Retrieve the user",
},
},
}),
async (c) => {
// apit hit
//apiHit(c, {endpoint: "api/auth/register"});
let { username, email, password } = await c.req.json();
if (!username || !email || !password) {
return c.json({ success: false, message: "Credentials missing" }, 400);
}
// some usernames that should be ignored
const badActors = ["admin", "root"];
if (badActors.includes(username)) {
return c.json(
{
success: false,
message: `${username} is not a valid name to be registerd please try again`,
},
400
);
}
try {
const register = await registerUser(username, password, email);
return c.json(
{
success: register.success,
message: register.message,
user: register?.user,
},
200
);
} catch (error) {
console.log(error);
return c.json(
{
success: false,
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,34 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import hasCorrectRole from "../../middleware/roleCheck.js";
import { getAllUsersRoles } from "../../controllers/userAdmin/getAllUserRoles.js";
const app = new OpenAPIHono();
app.openapi(
createRoute({
tags: ["Auth:admin"],
summary: "Gets Users Roles",
method: "get",
path: "/allusersroles",
middleware: [
authMiddleware,
hasCorrectRole(["admin", "systemAdmin"], "admin"),
],
responses: responses(),
}),
async (c) => {
// apit hit
//apiHit(c, {endpoint: "api/auth/register"});
const allUsers: any = await getAllUsersRoles();
return c.json({
success: allUsers?.success,
message: allUsers?.message,
data: allUsers?.data,
});
}
);
export default app;

View File

@@ -0,0 +1,35 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { getAllUsers } from "../../controllers/userAdmin/getUsers.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import hasCorrectRole from "../../middleware/roleCheck.js";
const app = new OpenAPIHono();
app.openapi(
createRoute({
tags: ["Auth:admin"],
summary: "Gets Users",
method: "get",
path: "/allusers",
middleware: [
authMiddleware,
hasCorrectRole(["admin", "systemAdmin"], "admin"),
],
responses: responses(),
}),
async (c) => {
// apit hit
//apiHit(c, {endpoint: "api/auth/register"});
const allUsers: any = await getAllUsers();
return c.json({
success: allUsers?.success,
message: allUsers?.message,
data: allUsers?.data,
});
}
);
export default app;

View File

@@ -0,0 +1,80 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { setUserAccess } from "../../controllers/userRoles/setUserRoles.js";
import { apiHit } from "../../../../globalUtils/apiHits.js";
import { apiReturn } from "../../../../globalUtils/apiReturn.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import hasCorrectRole from "../../middleware/roleCheck.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
const app = new OpenAPIHono();
const responseSchema = z.object({
success: z.boolean().openapi({ example: true }),
message: z.string().optional().openapi({ example: "user access" }),
data: z.array(z.object({})).optional().openapi({ example: [] }),
});
const UserAccess = z.object({
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.openapi({ example: "smith034" }),
module: z.string().openapi({ example: "production" }),
role: z.string().openapi({ example: "viewer" }),
override: z.string().optional().openapi({ example: "secretString" }),
});
app.openapi(
createRoute({
tags: ["Auth:admin"],
summary: "Sets Users access",
method: "post",
path: "/setuseraccess",
middleware: [
authMiddleware,
hasCorrectRole(["admin", "systemAdmin"], "admin"),
],
description: "When logged in you will be able to grant new permissions",
request: {
body: {
content: {
"application/json": { schema: UserAccess },
},
},
},
responses: responses(),
}),
async (c) => {
//apiHit(c, { endpoint: "api/auth/setUserRoles" });
const { username, module, role, override } = await c.req.json();
try {
const access = await setUserAccess(
username,
module,
role,
override
);
//return apiReturn(c, true, access?.message, access?.data, 200);
return c.json(
{
success: access.success,
message: access.message,
data: [], //access?.data,
},
200
);
} catch (error) {
console.log(error);
//return apiReturn(c, false, "Error in setting the user access", error, 400);
return c.json(
{
success: false,
message: "Error in setting the user access",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,85 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { setUserAccess } from "../../controllers/userRoles/setUserRoles.js";
import { apiHit } from "../../../../globalUtils/apiHits.js";
import { apiReturn } from "../../../../globalUtils/apiReturn.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import hasCorrectRole from "../../middleware/roleCheck.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { updateUserADM } from "../../controllers/userAdmin/updateUserAdm.js";
const app = new OpenAPIHono();
const responseSchema = z.object({
success: z.boolean().openapi({ example: true }),
message: z.string().optional().openapi({ example: "user access" }),
data: z.array(z.object({})).optional().openapi({ example: [] }),
});
const UserAccess = z.object({
user_id: z.string().openapi({ example: "users UUID" }),
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.optional()
.openapi({ example: "smith034" }),
email: z
.string()
.email()
.optional()
.openapi({ example: "smith@example.com" }),
password: z
.string()
.optional()
.openapi({ example: "Password1!" }),
});
app.openapi(
createRoute({
tags: ["Auth:admin"],
summary: "updates a specific user",
method: "patch",
path: "/updateuser",
middleware: [
authMiddleware,
hasCorrectRole(["admin", "systemAdmin"], "admin"),
],
//description: "When logged in you will be able to grant new permissions",
request: {
body: {
content: {
"application/json": { schema: UserAccess },
},
},
},
responses: responses(),
}),
async (c) => {
//apiHit(c, { endpoint: "api/auth/setUserRoles" });
const userData = await c.req.json();
try {
const userUPD: any = await updateUserADM(userData);
//return apiReturn(c, true, access?.message, access?.data, 200);
return c.json(
{
success: userUPD.success,
message: userUPD.message,
data: userUPD.data,
},
200
);
} catch (error) {
console.log(error);
//return apiReturn(c, false, "Error in setting the user access", error, 400);
return c.json(
{
success: false,
message: "Error in setting the user access",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,56 @@
import { db } from "../../../../database/dbclient.js";
import { users } from "../../../../database/schema/users.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
import { setSysAdmin } from "../controllers/userRoles/setSysAdmin.js";
import { createPassword } from "./createPassword.js";
export const massAccountCreation = async () => {
/**
* This will create a new account for all users before if they are already in there it will update just there password.
*
*/
const user: any = [
// {
// username: "landa002",
// email: "Oscar.Landa@alpla.com",
// password: "Frostlike-Petri5-Ungreased!",
// },
];
for (let i = 0; i < user.length; i++) {
const updatedUser = {
username: user[i].username,
email: user[i].email,
password: await createPassword(user[i].password),
};
const { data, error } = await tryCatch(
db
.insert(users)
.values(updatedUser)
.onConflictDoUpdate({
target: users.username,
set: {
password: updatedUser.password,
email: updatedUser.email,
},
})
.returning({
user_id: users.user_id,
username: users.username,
})
);
await setSysAdmin(data, "systemAdmin");
if (error) {
createLog(
"error",
"lst",
"auth",
`There was an error creating ${user[i].username}`
);
}
}
};

View File

@@ -0,0 +1,18 @@
import bcrypt from "bcryptjs";
export const checkPassword = async (
currentPassword: string,
dbPassword: string
) => {
let decyptPass = "";
try {
decyptPass = atob(dbPassword);
} catch (error) {
console.log(error);
}
// encypt password
const pass: string | undefined = process.env.SECRET;
const checked = bcrypt.compareSync(pass + currentPassword, decyptPass);
return checked;
};

View File

@@ -0,0 +1,17 @@
import bcrypt from "bcryptjs";
export const createPassword = async (password: string) => {
// encypt password
let pass: string | undefined = process.env.SECRET;
let salt: string | undefined = process.env.SALTING;
if (!pass || !salt) {
pass = "error";
} else {
pass = bcrypt.hashSync(pass + password, parseInt(salt));
pass = btoa(pass);
}
return pass;
};

View File

@@ -0,0 +1,44 @@
/**
* check if the roles are in and if not add them.
* this will only run on a server start up
*/
import {db} from "../../../../database/dbclient.js";
import {roles} from "../../../../database/schema/roles.js";
import {createLog} from "../../logger/logger.js";
// "view", "technician", "supervisor","manager", "admin", "systemAdmin"
const newRoles = [
{name: "viewer"},
{name: "technician"},
{name: "supervisor"},
{name: "manager"},
{name: "admin"},
{name: "tester"},
{name: "systemAdmin"},
];
export const areRolesIn = async () => {
// get the roles
try {
const roleCheck = await db.select().from(roles);
if (roleCheck.length !== newRoles.length) {
try {
const newRole = await db
.insert(roles)
.values(newRoles)
.onConflictDoNothing() // this will only update the ones that are new :D
.returning({name: roles.name});
createLog(
"info",
"lst",
"auth",
`${JSON.stringify(newRole)}, "Roles were just added due to missing them on server startup"`
);
} catch (error) {
createLog("error", "lst", "auth", `There was an error adding new roles to the db, ${error}`);
}
}
} catch (error) {
createLog("error", "lst", "auth", `There was an error adding new roles to the db, ${error}`);
}
};

View File

@@ -0,0 +1,47 @@
import { format } from "date-fns-tz";
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { fakeEDIUpdate } from "../../sqlServer/querys/dataMart/fakeEDIUpdate.js";
export const getFakeEDI = async (address: string) => {
let fakeEDI: any = [];
let updatedQuery = fakeEDIUpdate;
if (address) {
createLog(
"info",
"datamart",
"datamart",
"The user requested a specific address."
);
updatedQuery = fakeEDIUpdate.replace(
"--and IdAdresse = 14",
`and IdAdresse = ${address}`
);
}
try {
fakeEDI = await query(updatedQuery, "Gets fakeEDI orders to be fixed");
const correctedData = fakeEDI.data.map((n: any) => {
return {
...n,
DeliveryDate: format(n.DeliveryDate, "M/d/yyyy HH:mm"),
};
});
return {
success: true,
message: "Current open orders",
data: correctedData,
};
} catch (error) {
console.log(error);
return {
success: false,
message: "There was an error open orders",
data: error,
};
}
};

View File

@@ -0,0 +1,14 @@
import { query } from "../../sqlServer/prodSqlServer.js";
import { activeArticle } from "../../sqlServer/querys/dataMart/article.js";
export const getActiveAv = async () => {
let articles: any = [];
try {
const res = await query(activeArticle, "Get active articles");
articles = res?.data;
} catch (error) {
articles = error;
}
return articles;
};

View File

@@ -0,0 +1,41 @@
import { query } from "../../sqlServer/prodSqlServer.js";
import { customerInvNoHold } from "../../sqlServer/querys/dataMart/customerInventoryQuerys.js";
export const getCurrentCustomerInv = async (data: any | null) => {
//console.log(data.customer[0]);
let updatedQuery = customerInvNoHold;
if (data.customer) {
//console.log(data.customer);
updatedQuery = customerInvNoHold.replaceAll(
"--and IdAdressen",
`and IdAdressen = ${data.customer[0]}`
);
}
if (data.whseToInclude) {
updatedQuery = updatedQuery.replaceAll(
"--and x.IdWarenlager in (14,15)",
`and x.IdWarenlager in (${data.whseToInclude[0]})`
);
}
try {
const inventory: any = await query(
updatedQuery,
"Get active inventory"
);
return {
success: true,
message: "All customer inventory minus holds",
data: inventory.data,
};
} catch (error) {
return {
success: false,
message: "There was an error getting the inventory",
data: error,
};
}
};

View File

@@ -0,0 +1,84 @@
import { eq } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { settings } from "../../../../database/schema/settings.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { deliveryByDateRange } from "../../sqlServer/querys/dataMart/deleveryByDateRange.js";
import { addDays, format } from "date-fns";
export const getDeliveryByDateRange = async (data: any | null) => {
// const { data: plantToken, error: plantError } = await tryCatch(
// db.select().from(settings).where(eq(settings.name, "plantToken"))
// );
// if (plantError) {
// return {
// success: false,
// message: "Error getting Settings",
// data: plantError,
// };
// }
let deliverys: any = [];
let updatedQuery = deliveryByDateRange;
// start days can be sent over
if (data?.start) {
updatedQuery = updatedQuery.replaceAll("[startDate]", data.start[0]);
} else {
updatedQuery = updatedQuery.replaceAll("[startDate]", "1990-1-1");
}
// end days can be sent over
if (data?.end) {
updatedQuery = updatedQuery.replaceAll("[endDate]", data.end[0]);
} else {
const defaultEndDate = format(
addDays(new Date(Date.now()), 5),
"yyyy-M-d"
);
updatedQuery = updatedQuery.replaceAll("[endDate]", defaultEndDate);
}
try {
const res: any = await query(
updatedQuery,
"Get Delivery by date range"
);
deliverys = res.data;
//console.log(res.data);
} catch (error) {
console.log(error);
return {
success: false,
message: "All Deliveries within the range.",
data: error,
};
}
if (!data) {
deliverys = deliverys.splice(1000, 0);
}
// add plant token in
// const pOrders = deliverys.map((item: any) => {
// // const dateCon = new Date(item.loadingDate).toLocaleString("en-US", {
// // month: "numeric",
// // day: "numeric",
// // year: "numeric",
// // hour: "2-digit",
// // minute: "2-digit",
// // hour12: false,
// // });
// //const dateCon = new Date(item.loadingDate).toISOString().replace("T", " ").split(".")[0];
// const dateCon = new Date(item.loadingDate).toISOString().split("T")[0];
// //const delDate = new Date(item.deliveryDate).toISOString().replace("T", " ").split(".")[0];
// const delDate = new Date(item.deliveryDate).toISOString().split("T")[0];
// return {
// plantToken: plantToken[0].value,
// ...item,
// loadingDate: dateCon,
// deliveryDate: delDate,
// };
// });
return { success: true, message: "Current open orders", data: deliverys };
};

View File

@@ -0,0 +1,18 @@
import { desc } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { fifoIndex } from "../../../../database/schema/fifoIndex.js";
export const getFifoIndex = async () => {
let articles: any = [];
try {
const res = await db
.select()
.from(fifoIndex)
.orderBy(desc(fifoIndex.add_Date));
articles = res;
} catch (error) {
articles = error;
}
return articles;
};

View File

@@ -0,0 +1,24 @@
import { format } from "date-fns-tz";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { financeAudit } from "../../sqlServer/querys/dataMart/financeAudit.js";
export const getfinanceAudit = async (date: any) => {
let inventoryAudit: any = [];
const { data, error } = (await tryCatch(
query(financeAudit.replace("[date]", date), "inventory audit")
)) as any;
//console.log(data);
if (error) {
return [];
}
inventoryAudit = data.data.map((i: any) => {
return { ...i, bookinDate: format(i.bookinDate, "MM/dd/yyyy") };
});
return inventoryAudit;
};

View File

@@ -0,0 +1,73 @@
import { eq } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { settings } from "../../../../database/schema/settings.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { openOrders } from "../../sqlServer/querys/dataMart/openOrders.js";
import { serverSettings } from "../../server/controller/settings/getSettings.js";
export const getOpenOrders = async (data: any | null) => {
// const { data: plantToken, error: plantError } = await tryCatch(
// db.select().from(settings).where(eq(settings.name, "plantToken"))
// );
// if (plantError) {
// return {
// success: false,
// message: "Error getting Settings",
// data: plantError,
// };
// }
const plantToken = serverSettings.filter((n) => n.name === "plantToken");
let orders: any = [];
let updatedQuery = openOrders;
// start days can be sent over
if (data?.sDay) {
updatedQuery = updatedQuery.replaceAll("[sDay]", data.sDay[0]);
} else {
updatedQuery = updatedQuery.replaceAll("[sDay]", "15");
}
// end days can be sent over
if (data?.eDay) {
updatedQuery = updatedQuery.replaceAll("[eDay]", data.eDay[0]);
} else {
updatedQuery = updatedQuery.replaceAll("[eDay]", "5");
}
try {
orders = await query(updatedQuery, "Get active openorders");
} catch (error) {
return {
success: false,
message: "Errot getting current open orders",
data: [],
};
}
// add plant token in
const pOrders = orders.data.map((item: any) => {
// const dateCon = new Date(item.loadingDate).toLocaleString("en-US", {
// month: "numeric",
// day: "numeric",
// year: "numeric",
// hour: "2-digit",
// minute: "2-digit",
// hour12: false,
// });
//const dateCon = new Date(item.loadingDate).toISOString().replace("T", " ").split(".")[0];
const dateCon = new Date(item.loadingDate).toISOString().split("T")[0];
//const delDate = new Date(item.deliveryDate).toISOString().replace("T", " ").split(".")[0];
const delDate = new Date(item.deliveryDate).toISOString().split("T")[0];
return {
plantToken: plantToken[0].value,
...item,
loadingDate: dateCon,
deliveryDate: delDate,
};
});
return { success: true, message: "Current open orders", data: pOrders };
};

View File

@@ -0,0 +1,34 @@
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import {
totalInvNoRn,
totalInvRn,
} from "../../sqlServer/querys/dataMart/totalINV.js";
export const getINV = async (rn: boolean) => {
let inventory: any = [];
let updatedQuery = totalInvNoRn;
if (rn) {
createLog(
"info",
"datamart",
"datamart",
"The user requested the running numbers this could take a while."
);
updatedQuery = totalInvRn;
}
try {
inventory = await query(updatedQuery, "Gets Curruent inv");
return { success: true, message: "Current inv", data: inventory.data };
} catch (error) {
console.log(error);
return {
success: false,
message: "There was an error getting the inventory",
data: error,
};
}
};

View File

@@ -0,0 +1,47 @@
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { articleInfo } from "../../sqlServer/querys/psiReport/articleData.js";
// type ArticleData = {
// id: string
// }
export const getGetPSIArticleData = async (avs: string) => {
let articles: any = [];
if (!avs) {
return {
success: false,
message: `Missing av's please send at least one over`,
data: [],
};
}
const { data, error } = (await tryCatch(
query(articleInfo.replace("[articles]", avs), "PSI article info")
)) as any;
if (error) {
createLog(
"error",
"datamart",
"datamart",
`There was an error getting the article info: ${JSON.stringify(
error
)}`
);
return {
success: false,
messsage: `There was an error getting the article info`,
data: error,
};
}
articles = data.data;
return {
success: true,
message: "PSI Article Data",
data: articles,
};
};

View File

@@ -0,0 +1,63 @@
import { and, between, inArray, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
// type ArticleData = {
// id: string
// }
export const psiGetInventory = async (
avs: string,
startDate: string,
endDate: string
) => {
let articles: any = [];
if (!avs) {
return {
success: false,
message: `Missing av's please send at least one over`,
data: [],
};
}
const ids = avs.split(",").map((id) => id.trim());
const { data, error } = (await tryCatch(
db
.select()
.from(invHistoricalData)
.where(
and(
inArray(invHistoricalData.article, ids),
between(invHistoricalData.histDate, startDate, endDate)
)
)
//.limit(100)
)) as any;
if (error) {
createLog(
"error",
"datamart",
"datamart",
`There was an error getting the planning info: ${JSON.stringify(
error
)}`
);
return {
success: false,
messsage: `There was an error getting the planning info`,
data: error,
};
}
articles = data;
console.log(articles.length);
return {
success: true,
message: "PSI planning Data",
data: articles,
};
};

View File

@@ -0,0 +1,63 @@
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { planningNumbersByAVDate } from "../../sqlServer/querys/psiReport/planningNumbersByAv.js";
// type ArticleData = {
// id: string
// }
export const psiGetPlanningData = async (
avs: string,
startDate: string,
endDate: string
) => {
let articles: any = [];
if (!avs) {
return {
success: false,
message: `Missing av's please send at least one over`,
data: [],
};
}
const { data, error } = (await tryCatch(
query(
planningNumbersByAVDate
.replace("[articles]", avs)
.replace("[startDate]", startDate)
.replace("[endDate]", endDate),
"PSI planning info"
)
)) as any;
if (error) {
createLog(
"error",
"datamart",
"datamart",
`There was an error getting the planning info: ${JSON.stringify(
error
)}`
);
return {
success: false,
messsage: `There was an error getting the planning info`,
data: error,
};
}
articles = data.data;
return {
success: true,
message: "PSI planning Data",
data: articles.map((n: any) => {
if (n.PalDay) {
return { ...n, PalDay: n.PalDay.toFixed(2) };
}
return n;
}),
};
};

View File

@@ -0,0 +1,63 @@
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { productionNumbers } from "../../sqlServer/querys/psiReport/prodcuctionNumbers.js";
// type ArticleData = {
// id: string
// }
export const psiGetProductionData = async (
avs: string,
startDate: string,
endDate: string
) => {
let articles: any = [];
if (!avs) {
return {
success: false,
message: `Missing av's please send at least one over`,
data: [],
};
}
const { data, error } = (await tryCatch(
query(
productionNumbers
.replace("[articles]", avs)
.replace("[startDate]", startDate)
.replace("[endDate]", endDate),
"PSI production info"
)
)) as any;
if (error) {
createLog(
"error",
"datamart",
"datamart",
`There was an error getting the planning info: ${JSON.stringify(
error
)}`
);
return {
success: false,
messsage: `There was an error getting the planning info`,
data: error,
};
}
articles = data.data;
return {
success: true,
message: "PSI planning Data",
data: articles.map((n: any) => {
if (n.PalDay) {
return { ...n, PalDay: n.PalDay.toFixed(2) };
}
return n;
}),
};
};

View File

@@ -0,0 +1,21 @@
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { validateCityState } from "../../sqlServer/querys/dataMart/validatecityState.js";
export const validateCS = async () => {
let cs: any = [];
let updatedQuery = validateCityState;
try {
cs = await query(updatedQuery, "Get address data");
return { success: true, message: "City State Data", data: cs.data };
} catch (error) {
console.log(error);
return {
success: false,
message: "There was an error getting city state data.",
data: error,
};
}
};

View File

@@ -0,0 +1,40 @@
import { OpenAPIHono } from "@hono/zod-openapi";
import activequerys from "./route/getCurrentQuerys.js";
import getArticles from "./route/getActiveArticles.js";
import currentInv from "./route/getInventory.js";
import getCustomerInv from "./route/getCustomerInv.js";
import getOpenOrders from "./route/getOpenOrders.js";
import getDeliveryByDate from "./route/getDeliveryDateByRange.js";
import fakeEDI from "./route/fakeEDI.js";
import addressCorrections from "./route/getCityStateData.js";
import fifoIndex from "./route/getFifoIndex.js";
import financeAudit from "./route/getFinanceAudit.js";
import psiArticleData from "./route/getPsiArticleData.js";
import psiPlanningData from "./route/getPsiPlanningData.js";
import psiProductionData from "./route/getPsiProductionData.js";
import psiInventory from "./route/getPsiinventory.js";
const app = new OpenAPIHono();
const routes = [
activequerys,
getArticles,
currentInv,
getCustomerInv,
getOpenOrders,
getDeliveryByDate,
fakeEDI,
addressCorrections,
fifoIndex,
financeAudit,
psiArticleData,
psiPlanningData,
psiProductionData,
psiInventory,
] as const;
const appRoutes = routes.forEach((route) => {
app.route("/datamart", route);
});
export default app;

View File

@@ -0,0 +1,54 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getINV } from "../controller/getinventory.js";
import { getFakeEDI } from "../controller/fakeEDIUpdate.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
address: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns all open orders that need to be updated in fake edi.",
method: "get",
path: "/fakeediupdate",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const address: string = c.req.query("address") ?? "";
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/fakeediupdate" });
const { data, error } = await tryCatch(
getFakeEDI(address.toString() ?? "")
);
if (error) {
return c.json(
{
success: false,
message: "There was an error getting the inv.",
data: error,
},
400
);
}
return c.json({
success: data.success,
message: data.message,
data: data.data,
});
}
);
export default app;

View File

@@ -0,0 +1,47 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { getActiveAv } from "../controller/getActiveArticles.js";
const app = new OpenAPIHono({ strict: false });
const EomStat = z.object({
plant: z.string().openapi({ example: "Salt Lake City" }),
userRan: z.string().openapi({ example: "smith034" }),
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns all the Active articles.",
method: "get",
path: "/getarticles",
responses: responses(),
}),
async (c) => {
//const body = await c.req.json();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/getarticles" });
try {
return c.json(
{
success: true,
message: "Current active Articles",
data: await getActiveAv(),
},
200
);
} catch (error) {
return c.json(
{
success: false,
message: "There was an error posting the eom stat.",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,50 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getINV } from "../controller/getinventory.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { validateCS } from "../controller/validateCityState.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns Address with incorrect city state.",
method: "get",
path: "/getaddressdata",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/getaddressdata" });
const { data, error } = await tryCatch(validateCS());
if (error) {
return c.json(
{
success: false,
message: "There was an error getting address data.",
data: error,
},
400
);
}
return c.json({
success: data.success,
message: data.message,
data: data.data,
});
}
);
export default app;

View File

@@ -0,0 +1,146 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
const app = new OpenAPIHono({ strict: false });
const current: any = [
{
name: "getActiveAv",
endpoint: "/api/datamart/getarticles",
description: "Gets all current active AV, with specific critiera.",
},
// {
// name: "getStockLaneDims",
// endpoint: "/api/v1/masterData/getStockDims",
// description: "Returns the lane dims along with a column to send actaul dims to be updated.",
// },
// {
// name: "getAddressInfo",
// endpoint: "/api/v1/masterData/getAddressInfo",
// description: "Returns current active addresses with street and zip",
// },
// {
// name: "getMissingPkgData",
// endpoint: "/api/v1/masterData/getMissingPKGData",
// description: "Returns all packaging data that is missing either printer, layout, or carton layout",
// },
{
name: "getCustomerInventory",
endpoint: "/api/datamart/getcustomerinventory",
description:
"Returns specific customer inventory based on there address ID, with optional to include warehouses, IE 36,41,5. leaving warehouse blank will just pull everything",
criteria: "customer,whseToInclude",
},
// {
// name: "getPalletLabels",
// endpoint: "/api/v1/masterData/getPalletLabels",
// description: "Returns specific amount of pallets RN, Needs label number and printer, Specfic to Dayton.",
// criteria: "runningNumber,printerName,count",
// },
{
name: "getopenorders",
endpoint: "/api/datamart/getopenorders",
description:
"Returns open orders based on day count sent over, sDay 15 days in the past eDay 5 days in the future, can be left empty for this default days",
criteria: "sDay,eDay",
},
// {
// name: "getOpenIncoming",
// endpoint: "/api/v1/masterData/getOpenIncoming",
// description:
// "Returns open orders based on day count sent over, sDay 15 days in the past eDay 5 days in the future, can be left empty for this default days",
// criteria: "sDay,eDay",
// },
// {
// name: "planningCheckPkg",
// endpoint: "/api/v1/masterData/planningPkgCheck",
// description: "Returns all lots starting later than today and has a pkg that is missing layouts.",
// },
{
name: "getinventory",
endpoint: "/api/datamart/getinventory",
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
description:
"Returns all inventory, excludes inv locations. no running numbers",
criteria: "includeRunnningNumbers", // uncomment this out once the improt process can be faster
},
// {
// name: "getOpenOrderUpdates",
// endpoint: "/api/v1/masterData/getOpenOrderUpdates",
// // description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
// description: "Returns all orders based on customer id, leaving empty will pull everythinng in.",
// criteria: "customer", // uncomment this out once the improt process can be faster
// },
{
name: "getSiloAdjustment",
endpoint: "/api/logistics/getsilosdjustment",
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
description:
"Returns all siloadjustments in selected date range IE: 1/1/2025 to 1/31/2025",
criteria: "startDate,endDate", // uncomment this out once the improt process can be faster
},
{
name: "Delivery by date trange",
endpoint: "/api/datamart/deliverybydaterange",
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
description:
"Returns all Deliverys in selected date range IE: 1/1/2025 to 1/31/2025",
criteria: "start,end", // uncomment this out once the improt process can be faster
},
{
name: "Fake Edi Update",
endpoint: "/api/datamart/fakeediupdate",
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
description:
"Returns all open orders to correct and resubmit, leaving blank will get everything putting an address only returns the specified address",
criteria: "address", // uncomment this out once the improt process can be faster
},
{
name: "Address Corrections",
endpoint: "/api/datamart/getaddressdata",
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
description:
"Returns all addresses that will not process correctly in tms due to incorrect city state setup.",
//criteria: "address", // uncomment this out once the improt process can be faster
},
{
name: "Fifo index",
endpoint: "/api/datamart/getfifoindex",
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
description:
"Returns fifo index for all pallets shipped within the last 90 days.",
//criteria: "address", // uncomment this out once the improt process can be faster
},
{
name: "Finance Audit inv",
endpoint: "/api/datamart/getfinanceaudit",
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
description:
"Returns all inventory past the date provided, ie: 5/31/2025",
criteria: "date", // uncomment this out once the improt process can be faster
},
];
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns all avalible querys.",
method: "get",
path: "/getavalibleaquerys",
responses: responses(),
}),
async (c) => {
//const body = await c.req.json();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/getavalibleaquerys" });
return c.json({
success: true,
message: "All Current Active Querys.",
sheetVersion: 2.8,
data: current,
});
}
);
export default app;

View File

@@ -0,0 +1,54 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getINV } from "../controller/getinventory.js";
import { getCurrentCustomerInv } from "../controller/getCustomerInventory.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns All customer minus holds.",
method: "get",
path: "/getcustomerinventory",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const customerData: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/getcustomerinventory" });
const { data, error } = await tryCatch(
getCurrentCustomerInv(customerData ? customerData : null)
);
if (error) {
return c.json(
{
success: false,
message: "There was an error getting the inv.",
data: error,
},
400
);
}
return c.json({
success: data.success,
message: data.message,
data: data.data,
});
}
);
export default app;

View File

@@ -0,0 +1,54 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getDeliveryByDateRange } from "../controller/getDeliveryByDateRange.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns deliverys by daterange.",
method: "get",
path: "/deliverybydaterange",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const delivery: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/deliverybydaterange" });
const { data, error } = await tryCatch(
getDeliveryByDateRange(delivery ? delivery : null)
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the deliveries.",
data: error,
},
400
);
}
return c.json({
success: data.success,
message: data.message,
data: data.data,
});
}
);
export default app;

View File

@@ -0,0 +1,48 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { getActiveAv } from "../controller/getActiveArticles.js";
import { getFifoIndex } from "../controller/getFifoIndex.js";
const app = new OpenAPIHono({ strict: false });
const EomStat = z.object({
plant: z.string().openapi({ example: "Salt Lake City" }),
userRan: z.string().openapi({ example: "smith034" }),
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns all the fifo data.",
method: "get",
path: "/getfifoindex",
responses: responses(),
}),
async (c) => {
//const body = await c.req.json();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/getfifoindex" });
try {
return c.json(
{
success: true,
message: "Fifo index data for the last 90days",
data: await getFifoIndex(),
},
200
);
} catch (error) {
return c.json(
{
success: false,
message: "There was an error getting fifo index data.",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,69 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { getfinanceAudit } from "../controller/getFinanceAudit.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
const app = new OpenAPIHono({ strict: false });
const EomStat = z.object({
plant: z.string().openapi({ example: "Salt Lake City" }),
userRan: z.string().openapi({ example: "smith034" }),
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary:
"Returns inventory to date and current sales prices and booking date of the inventory.",
method: "get",
path: "/getfinanceaudit",
responses: responses(),
}),
async (c: any) => {
//const body = await c.req.json();
// make sure we have a vaid user being accessed thats really logged in
const { data, error } = (await tryCatch(c.req.query())) as any;
//console.log(data.date);
if (error) {
return {
success: false,
message: "Missing Mandatory Data",
data: error,
};
}
if (!data.date) {
return {
success: false,
message: "Missing Date",
data: [],
};
}
apiHit(c, { endpoint: "/getfinanceaudit", lastBody: data });
try {
return c.json(
{
success: true,
message: `Inventory older than ${data.date}`,
data: await getfinanceAudit(data.date),
},
200
);
} catch (error) {
return c.json(
{
success: false,
message: "There was an error getting inventory data",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,54 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getINV } from "../controller/getinventory.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns All current inventory.",
method: "get",
path: "/getinventory",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const includeRunnningNumbers: string =
c.req.query("includeRunnningNumbers") ?? "";
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/getinventory" });
const { data, error } = await tryCatch(
getINV(includeRunnningNumbers?.length > 0 ? true : false)
);
if (error) {
return c.json(
{
success: false,
message: "There was an error getting the inv.",
data: error,
},
400
);
}
return c.json({
success: data.success,
message: data.message,
data: data.data,
});
}
);
export default app;

View File

@@ -0,0 +1,54 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getOpenOrders } from "../controller/getOpenOrders.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
const app = new OpenAPIHono({ strict: false });
// const Body = z.object({
// includeRunnningNumbers: z.string().openapi({ example: "x" }),
// });
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns All open orders.",
method: "get",
path: "/getopenorders",
// request: {
// body: {
// content: {
// "application/json": { schema: Body },
// },
// },
// },
responses: responses(),
}),
async (c) => {
const customer: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/getopenorders" });
const { data, error } = await tryCatch(
getOpenOrders(customer ? customer : null)
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the inv.",
data: error,
},
400
);
}
return c.json({
success: data.success,
message: data.message,
data: data.data,
});
}
);
export default app;

View File

@@ -0,0 +1,61 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getDeliveryByDateRange } from "../controller/getDeliveryByDateRange.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { getGetPSIArticleData } from "../controller/psiGetArticleData.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns the psiarticleData.",
method: "get",
path: "/psiarticledata",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const articles: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/psiarticledata" });
//console.log(articles["avs"][0]);
const { data, error } = await tryCatch(
getGetPSIArticleData(articles ? articles["avs"][0] : null)
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the articles.",
data: error,
},
400
);
}
//console.log(data);
return c.json(
{
success: data.success,
message: data.message,
data: data.data,
},
data.success ? 200 : 400
);
}
);
export default app;

View File

@@ -0,0 +1,64 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { psiGetPlanningData } from "../controller/psiGetPlanningData.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns the psiarticleData.",
method: "get",
path: "/psiplanningdata",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const q: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/psiplanningdata" });
//console.log(articles["avs"][0]);
const { data, error } = await tryCatch(
psiGetPlanningData(
q["avs"] ? q["avs"][0] : null,
q["startDate"] ? q["startDate"][0] : null,
q["endDate"] ? q["endDate"][0] : null
)
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the planning.",
data: error,
},
400
);
}
//console.log(data);
return c.json(
{
success: data.success,
message: data.message,
data: data.data,
},
data.success ? 200 : 400
);
}
);
export default app;

View File

@@ -0,0 +1,64 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { psiGetProductionData } from "../controller/psiGetProductionData.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns the psiproductiondata.",
method: "get",
path: "/psiproductiondata",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const q: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/psiproductiondata" });
//console.log(articles["avs"][0]);
const { data, error } = await tryCatch(
psiGetProductionData(
q["avs"] ? q["avs"][0] : null,
q["startDate"] ? q["startDate"][0] : null,
q["endDate"] ? q["endDate"][0] : null
)
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the production.",
data: error,
},
400
);
}
//console.log(data);
return c.json(
{
success: data.success,
message: data.message,
data: data.data,
},
data.success ? 200 : 400
);
}
);
export default app;

View File

@@ -0,0 +1,64 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { psiGetInventory } from "../controller/psiGetInventory.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns the getPsiinventory.",
method: "get",
path: "/getpsiinventory",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const q: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/getpsiinventory" });
//console.log(articles["avs"][0]);
const { data, error } = await tryCatch(
psiGetInventory(
q["avs"] ? q["avs"][0] : null,
q["startDate"] ? q["startDate"][0] : null,
q["endDate"] ? q["endDate"][0] : null
)
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the production.",
data: error,
},
400
);
}
//console.log(data);
return c.json(
{
success: data.success,
message: data.message,
data: data.data,
},
data.success ? 200 : 400
);
}
);
export default app;

View File

@@ -0,0 +1,139 @@
// import cron from "node-cron";
// import {runQuery, prisma, totalInvNoRn, activeArticle, getShiftTime, historicalInv} from "database";
// import {createLog} from "logging";
// import {deleteHistory} from "./deleteHistory.js";
// export const historyInv = async (date) => {
// //console.log(date);
// if (!date) {
// return `Missing Data`;
// }
// // date should be sent over as a string IE: 2024-01-01
// let inv = [];
// try {
// inv = await prisma.historyInventory.findMany({where: {histDate: date}});
// console.log(inv.length);
// // if the date returns nothing we need to pull the historical data
// if (inv.length === 0) {
// const result = await prisma.settings.findFirst({where: {name: "plantToken"}});
// try {
// const plantUpdate = historicalInv.replaceAll("test1", result.value);
// const queryDate = plantUpdate.replaceAll("[date]", date);
// inv = await runQuery(queryDate, "Get histical inv");
// return inv;
// } catch (error) {
// createLog("general/eom", "error", "There was an error getting the historical inv.");
// return error;
// }
// } else {
// return inv;
// }
// //return inv;
// } catch (error) {
// console.log(error);
// return error;
// }
// };
// // start the cron job for getting the hostrical inv based on the plants shift time
// export const startCronHist = () => {
// let shiftTime = ["06", "00", "00"];
// const startProcess = async () => {
// let inv = [];
// let articles = [];
// let plantToken = "test1";
// const date = new Date();
// const dateString = date.toISOString().split("T")[0];
// date.setDate(date.getDate() - 30);
// const oldDate = date.toISOString().split("T")[0];
// // checking if even need to run this
// // before adding more make sure we dont already have data
// const checkInv = await prisma.historyInventory.findFirst({where: {histDate: dateString}});
// if (checkInv) {
// createLog(
// "general/eom",
// "warn",
// `There seems to already be inventory added for ${dateString}, no new data will be added`
// );
// return;
// }
// // get plant token
// try {
// const result = await prisma.settings.findFirst({where: {name: "plantToken"}});
// plantToken = result.value;
// } catch (error) {
// createLog("general/eom", "error", "failed to get planttoken");
// }
// //get shift time
// try {
// const result = await runQuery(getShiftTime.replaceAll("test1", plantToken), "GettingShift time");
// shiftTime = result[0].shiftStartTime.split(":");
// } catch (error) {
// createLog("general/eom", "error", `Error running getShift Query: ${error}`);
// }
// // get inventory
// try {
// const result = await runQuery(totalInvNoRn.replaceAll("test1", plantToken), "getting inventory");
// inv = result;
// } catch (error) {
// createLog("general/eom", "error", `Error running get inventory Query: ${error}`);
// }
// // get active articles
// try {
// const result = await runQuery(activeArticle.replaceAll("test1", plantToken), "Get active articles");
// articles = result;
// } catch (error) {
// createLog("general/eom", "error", `Error running get article: ${error}`);
// }
// //add the inventory to the historical table
// try {
// let hist = Object.entries(inv).map(([key, value]) => {
// // remove the values we dont want in the historical view
// const {total_Pallets, avalible_Pallets, coa_Pallets, held_Pallets, ...histData} = value;
// // get av tyep
// const avType = articles.filter((a) => (a.IdArtikelvarianten = inv[key].av))[0].TypeOfMaterial;
// // add in the new fields
// const hist = {
// ...histData,
// histDate: dateString, //new Date(Date.now()).toISOString().split("T")[0],
// avType,
// };
// return hist;
// });
// try {
// const addHistData = await prisma.historyInventory.createMany({data: hist});
// createLog(
// "general/eom",
// "info",
// `${addHistData.count} were just added to the historical inventory for date ${dateString}`
// );
// } catch (error) {
// createLog("general/eom", "error", `Adding new historical inventory error: ${error}`);
// }
// // delete the older inventory
// deleteHistory(oldDate);
// } catch (error) {
// createLog("general/eom", "error", `Adding new historical inventory error: ${error}`);
// }
// };
// // actaully run the process once after restaart just to make sure we have inventory
// startProcess();
// // setup the cron stuff
// const startHour = shiftTime[0];
// const startMin = shiftTime[1];
// createLog("general/eom", "info", `Historical Data will run at ${shiftTime[0]}:${shiftTime[1]} daily`);
// cron.schedule(`${startMin} ${startHour} * * *`, () => {
// createLog("general/eom", "info", "Running historical invnetory.");
// startProcess();
// });
// };

View File

@@ -0,0 +1,32 @@
import { eq } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { format } from "date-fns";
export const historicalInvByDate = async (date: string) => {
const histDate = new Date(date);
const { data, error } = (await tryCatch(
db
.select()
.from(invHistoricalData)
.where(
eq(invHistoricalData.histDate, format(histDate, "yyyy-MM-dd"))
)
)) as any;
if (error) {
return {
success: false,
message: "There was an error with getting the inventory",
data: error,
};
}
return {
success: true,
message: `Historical inventory for ${date}`,
data: data,
};
};

View File

@@ -0,0 +1,24 @@
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { format } from "date-fns";
import { query } from "../../sqlServer/prodSqlServer.js";
import { lastPurchasePrice } from "../../sqlServer/querys/eom/lstPurchasePrice.js";
export const lastPurchase = async () => {
const { data, error } = (await tryCatch(
query(lastPurchasePrice, "Last purchase price")
)) as any;
if (error) {
return {
success: false,
message: "Error getting the last purchase price",
data: error,
};
}
return {
success: true,
message: `Last purchase price for all av in the last 5 years`,
data: data.data,
};
};

View File

@@ -0,0 +1,23 @@
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { lastSalesPriceCheck } from "../../sqlServer/querys/eom/lastSalesprice.js";
export const lastSales = async (date: string) => {
const { data, error } = (await tryCatch(
query(lastSalesPriceCheck.replace("[date]", date), "Last sales price")
)) as any;
if (error) {
return {
success: false,
message: "Error getting the last sales price",
data: error,
};
}
return {
success: true,
message: `Last sales price for all av in the last 5 years`,
data: data.data,
};
};

View File

@@ -0,0 +1,51 @@
import { OpenAPIHono } from "@hono/zod-openapi";
const app = new OpenAPIHono();
import stats from "./route/stats.js";
import history from "./route/invHistory.js";
import { createJob } from "../notifications/utils/processNotifications.js";
import { historicalInvIMmport } from "./utils/historicalInv.js";
import { tryCatch } from "../../globalUtils/tryCatch.js";
import { query } from "../sqlServer/prodSqlServer.js";
import { shiftChange } from "../sqlServer/querys/misc/shiftChange.js";
import { createLog } from "../logger/logger.js";
import lastPurch from "./route/getLastPurchPrice.js";
import lastSales from "./route/getLastSalesPrice.js";
const routes = [stats, history, lastPurch, lastSales] as const;
const appRoutes = routes.forEach((route) => {
app.route("/eom", route);
});
setTimeout(async () => {
const { data: shift, error: shiftError } = (await tryCatch(
query(shiftChange, "shift change from material.")
)) as any;
if (shiftError) {
createLog(
"error",
"eom",
"eom",
"There was an error getting the shift times will use fallback times"
);
}
// shift split
const shiftTimeSplit = shift?.data[0]?.shiftChange.split(":");
const cronSetup = `${
shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[1])}` : "0"
} ${
shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[0])}` : "7"
} * * *`;
//console.log(cronSetup);
createJob("eom_historical_inv", cronSetup, historicalInvIMmport);
}, 5 * 1000);
// the time we want to run the hostircal data should be the same time the historical data run on the server
// getting this from the shift time
export default app;

View File

@@ -0,0 +1,41 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { lastPurchase } from "../controller/getLastPurchasesPrice.js";
const app = new OpenAPIHono({ strict: false });
app.openapi(
createRoute({
tags: ["eom"],
summary: "Returns last sales price.",
method: "get",
path: "/lastpurchprice",
responses: responses(),
}),
async (c) => {
//const body = await c.req.json();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/lastpurchprice" });
try {
const res = await lastPurchase();
return c.json(
{ success: res.success, message: res.message, data: res.data },
200
);
} catch (error) {
return c.json(
{
success: false,
message: "There was an error posting the eom stat.",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,43 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { lastPurchase } from "../controller/getLastPurchasesPrice.js";
import { lastSales } from "../controller/getLastestSalesPrice.js";
const app = new OpenAPIHono({ strict: false });
app.openapi(
createRoute({
tags: ["eom"],
summary: "Returns last sales price.",
method: "get",
path: "/lastsalesprice",
responses: responses(),
}),
async (c) => {
//const body = await c.req.json();
const month: string = c.req.query("month") ?? "";
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/lastsalesprice" });
try {
const res = await lastSales(month);
return c.json(
{ success: res.success, message: res.message, data: res.data },
200
);
} catch (error) {
return c.json(
{
success: false,
message: "There was an error posting the eom stat.",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,46 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { historicalInvByDate } from "../controller/getHistoricalInvByDate.js";
const app = new OpenAPIHono({ strict: false });
const EomStat = z.object({
plant: z.string().openapi({ example: "Salt Lake City" }),
userRan: z.string().openapi({ example: "smith034" }),
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
});
app.openapi(
createRoute({
tags: ["eom"],
summary: "Gets History Data by date.",
method: "get",
path: "/histinv",
responses: responses(),
}),
async (c) => {
//const body = await c.req.json();
// make sure we have a vaid user being accessed thats really logged in
const month: string = c.req.query("month") ?? "";
apiHit(c, { endpoint: "/histinv" });
try {
const res = await historicalInvByDate(month);
return c.json(
{ success: res.success, message: res.message, data: res.data },
200
);
} catch (error) {
return c.json(
{
success: false,
message: "There was an error posting the eom stat.",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,41 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
const app = new OpenAPIHono({ strict: false });
const EomStat = z.object({
plant: z.string().openapi({ example: "Salt Lake City" }),
userRan: z.string().openapi({ example: "smith034" }),
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
});
app.openapi(
createRoute({
tags: ["eom"],
summary: "Adds in the stats for the eom.",
method: "post",
path: "/stats",
request: {
params: EomStat,
},
responses: responses(),
}),
async (c) => {
//const body = await c.req.json();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/stats" });
try {
return c.json({ success: true, message: "", data: [] }, 200);
} catch (error) {
return c.json(
{
success: false,
message: "There was an error posting the eom stat.",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,114 @@
import { sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { totalInvNoRn } from "../../sqlServer/querys/dataMart/totalINV.js";
import { format } from "date-fns-tz";
import { serverSettings } from "../../server/controller/settings/getSettings.js";
import { deleteHistory } from "./removeHistorical.js";
import { activeArticle } from "../../sqlServer/querys/dataMart/article.js";
export const historicalInvIMmport = async () => {
const plantToken = serverSettings.filter((n) => n.name === "plantToken");
const { data, error } = (await tryCatch(
db.select().from(invHistoricalData)
)) as any;
if (error) {
createLog(
"error",
"eom",
"eom",
`There was an error getting the historical data`
);
}
// check if we have data already for today this way we dont duplicate anything.
const today = new Date();
today.setDate(today.getDate() - 1);
const dateCheck = data?.filter(
(i: any) => i.histDate === format(today, "yyyy-MM-dd")
);
if (dateCheck.length === 0) {
// get the historical data from the sql
const { data: inv, error: invError } = (await tryCatch(
query(totalInvNoRn, "eom historical data")
)) as any;
if (invError) {
createLog(
"error",
"eom",
"eom",
`There was an error getting the sql data`
);
return;
}
if (inv.data.length === 0) {
createLog("error", "eom", "eom", inv.message);
return;
}
const { data: articles, error: avError } = (await tryCatch(
query(activeArticle, "Get active articles")
)) as any;
const av = articles.data.length > 0 ? articles.data : ([] as any);
const importInv = inv.data ? inv.data : [];
const eomImportData = importInv.map((i: any) => {
return {
histDate: sql`(NOW() - INTERVAL '1 day')::date`,
plantToken: plantToken[0].value,
article: i.av,
articleDescription: i.Alias,
materialType:
av.filter((a: any) => a.IdArtikelvarianten === i.av)
.length > 0
? av.filter(
(a: any) => a.IdArtikelvarianten === i.av
)[0]?.TypeOfMaterial
: "Item not defined",
total_QTY: i.Total_PalletQTY,
avaliable_QTY: i.Avaliable_PalletQTY,
coa_QTY: i.COA_QTY,
held_QTY: i.Held_QTY,
consignment: i.Consigment,
lot_Number: i.lot,
};
});
const { data: dataImport, error: errorImport } = await tryCatch(
db.insert(invHistoricalData).values(eomImportData)
);
if (errorImport) {
createLog(
"error",
"eom",
"eom",
`There was an error importing all the inventory data.`
);
return;
}
if (dataImport) {
createLog(
"info",
"eom",
"eom",
`All data was imported succefully.`
);
return;
}
} else {
createLog("info", "eom", "eom", `Yesterdays Data already in..`);
}
// do the check to delete old data
deleteHistory();
};

View File

@@ -0,0 +1,51 @@
// import {prisma} from "database";
// import {createLog} from "logging";
import { lte, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
// export const deleteHistory = async (date: string) => {
// // delete the inventory if it equals this date
// try {
// const remove = await prisma.$executeRaw`
// DELETE FROM historyInventory
// WHERE histDate < ${date}
// `;
// createLog("general/eom", "info", `${remove} were just remove from the historical inventory for date: ${date}`);
// } catch (error) {
// createLog("general/eom", "error", `Removing historical inventory error: ${error}`);
// }
// };
export const deleteHistory = async () => {
const { data, error } = await tryCatch(
db
.delete(invHistoricalData)
.where(
lte(
invHistoricalData.histDate,
sql`(NOW() - INTERVAL '365 day')::date`
)
)
);
if (error) {
createLog(
"error",
"eom",
"eom",
"There was an error deleting the historical data."
);
return;
}
createLog(
"info",
"eom",
"eom",
"Data older than 45 days has been deleted."
);
};

View File

@@ -0,0 +1,52 @@
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
const app = new OpenAPIHono();
// Define the request body schema
const requestSchema = z.object({
ip: z.string().optional(),
endpoint: z.string().optional(),
action: z.string().optional(),
stats: z.string().optional(),
});
// Define the response schema
const responseSchema = z.object({
message: z.string(),
});
app.openapi(
createRoute({
tags: ["api"],
summary: "Tracks the API posts and how often",
method: "post",
path: "/hits",
request: {
body: {
content: {
"application/json": {schema: requestSchema},
},
},
},
responses: {
200: {
content: {
"application/json": {schema: responseSchema},
},
description: "Response message",
},
},
}),
async (c) => {
const data = await c.req.json();
//apiHit(data);
// Return response with the received data
return c.json({
message: `Received name: ${data.name}, arrayData: ${data.arrayData}`,
});
}
);
export default app;

View File

@@ -0,0 +1,90 @@
import { OpenAPIHono } from "@hono/zod-openapi";
import { apiReference } from "@scalar/hono-api-reference";
import { settings } from "../../../../database/schema/settings.js";
import { db } from "../../../../database/dbclient.js";
import { eq } from "drizzle-orm";
import { getSettings } from "../../server/controller/settings/getSettings.js";
const app = new OpenAPIHono();
const serverSettings = await getSettings();
const plantToken = serverSettings.filter((n) => n.name === "plantToken") as any; //await db.select().from(settings).where(eq(settings.name, "plantToken"));
let pToken = plantToken[0]?.value;
const testServers = ["test1", "test2", "test3"];
if (testServers.includes(plantToken[0]?.value)) {
pToken = "usbow1";
}
app.get(
"/docs",
apiReference({
theme: "kepler",
layout: "classic",
defaultHttpClient: { targetKey: "node", clientKey: "axios" },
pageTitle: "Lst API Reference",
hiddenClients: [
"libcurl",
"clj_http",
"httpclient",
"restsharp",
"native",
"http1.1",
"asynchttp",
"nethttp",
"okhttp",
"unirest",
"xhr",
"fetch",
"jquery",
"okhttp",
"native",
"request",
"unirest",
"nsurlsession",
"cohttp",
"curl",
"guzzle",
"http1",
"http2",
"webrequest",
"restmethod",
"python3",
"requests",
"httr",
"native",
"curl",
"httpie",
"wget",
"nsurlsession",
"undici",
],
url: "/api/ref",
baseServerURL: "https://scalar.com",
servers: [
{
url: `http://${pToken}vms006:${process.env.VITE_SERVER_PORT}`,
description: "Production",
},
{
url: `http://localhost:${process.env.VITE_SERVER_PORT}`,
description: "dev server",
},
],
// authentication: {
// preferredSecurityScheme: {'bearerAuth'},
// },
// metaData: {
// title: "Page title",
// description: "My page page",
// ogDescription: "Still about my my page",
// ogTitle: "Page title",
// ogImage: "https://example.com/image.png",
// twitterCard: "summary_large_image",
// // Add more...
// },
})
);
export default app;

View File

@@ -0,0 +1,30 @@
import { eq, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { logs } from "../../../../database/schema/logs.js";
import { createLog } from "../logger.js";
export const clearLog = async (id: string) => {
/**
* mark the log as cleared
*/
try {
const clear = await db
.update(logs)
.set({ checked: true, created_at: sql`NOW()` })
.where(eq(logs.log_id, id));
createLog("info", "lst", "logger", "Log just cleared.");
return { success: true, message: "Log was just cleared." };
} catch (error) {
createLog(
"error",
"lst",
"logger",
"There was an error clearing the log."
);
return {
success: false,
message: "There was an error clearing the log.",
};
}
};

View File

@@ -0,0 +1,42 @@
import { and, desc, eq, gte, inArray, lte, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { logs } from "../../../../database/schema/logs.js";
import { createLog } from "../logger.js";
export const getLogs = async (data: any) => {
try {
// clear all remaining logs ne to info.
const checked =
data.checked && data.checked[0] === "true" ? true : false || false;
const logData = await db
.select()
.from(logs)
.where(
and(
gte(
logs.created_at,
sql.raw(`NOW() - INTERVAL '${data.hours ?? "4"} hours'`)
),
inArray(logs.service, data.service),
inArray(logs.level, data.level),
eq(logs.checked, checked)
)
)
.orderBy(desc(logs.created_at));
return { success: true, message: "logs returned", data: logData };
} catch (error) {
console.log(error);
createLog(
"error",
"lst",
"logger",
`There was an error deleteing server logs. ${error}`
);
return {
success: false,
message: "An error occured while trying to get the logs",
error,
};
}
};

View File

@@ -0,0 +1,65 @@
import { and, eq, inArray, lte, ne, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { logs } from "../../../../database/schema/logs.js";
import { createLog } from "../logger.js";
export const logCleanup = async () => {
/**
* We will run the clean logger where we have aged logs that do not need to be here flooding the db
*/
// clear the server logs older than 3 days
try {
// clear info logs older than 3 days
const delLogs = await db
.delete(logs)
.where(
and(
lte(logs.created_at, sql`NOW() - INTERVAL '3 days'`),
//inArray(logs.service, ["server", "tcp", "sqlProd", "globalutils","notify", "logger", "serverupdater"]),
eq(logs.level, "info")
)
)
.returning({ name: logs.message });
createLog(
"info",
"lst",
"logger",
`${delLogs.length} Server logs were just deleted that were older than 3 days`
);
} catch (error) {
createLog(
"error",
"lst",
"logger",
`There was an error deleteing server logs. ${error}`
);
}
try {
// clear all remaining logs ne to info.
const delLogs = await db
.delete(logs)
.where(
and(
lte(logs.created_at, sql`NOW() - INTERVAL '7 days'`),
//inArray(logs.service, ["server", "tcp", "sqlProd", "globalutils", "notify", "logger", "serverupdater"]),
ne(logs.level, "info")
)
)
.returning({ name: logs.message });
createLog(
"info",
"lst",
"logger",
`${delLogs.length} Server logs were just deleted that were older than 7 days`
);
} catch (error) {
createLog(
"error",
"lst",
"logger",
`There was an error deleteing server logs. ${error}`
);
}
};

View File

@@ -0,0 +1,29 @@
import type {Context} from "hono";
import {db} from "../../../../database/dbclient.js";
import {and, eq, gt} from "drizzle-orm";
import {streamSSE, streamText} from "hono/streaming";
import {logs} from "../../../../database/schema/logs.js";
export async function streamLogs(c: Context) {
let id = 0;
let running = true;
// c.header("Content-Type", "text/event-stream");
// c.header("Cache-Control", "no-cache");
// c.header("Connection", "keep-alive");
const getLogs = async () => {};
return streamSSE(c, async (stream) => {
while (running) {
const message = `It is ${new Date().toISOString()}`;
await stream.writeSSE({
data: message,
event: "time-update",
id: String(id++),
});
await stream.sleep(1000);
if (id === 5) {
running = false;
}
}
});
}

View File

@@ -0,0 +1,41 @@
import {db} from "../../../database/dbclient.js";
import {logs} from "../../../database/schema/logs.js";
import build from "pino-abstract-transport";
type Log = {
username: string | null;
service: string;
level: string;
msg: string;
};
const pinoLogLevels: any = {
10: "trace",
20: "debug",
30: "info",
40: "warn",
50: "error",
60: "fatal",
};
// Create a custom transport function
export default async function (log: Log) {
//const {username, service, level, msg, ...extra} = log;
try {
return build(async function (source) {
for await (let obj of source) {
// Insert log entry into the PostgreSQL database using Drizzle ORM
// convert to the name to make it more easy to find later :P
const levelName = pinoLogLevels[obj.level] || "unknown";
await db.insert(logs).values({
level: levelName,
username: obj?.username.toLowerCase(),
service: obj?.service.toLowerCase(),
message: obj.msg,
});
}
});
} catch (err) {
console.error("Error inserting log into database:", err);
}
}

View File

@@ -0,0 +1,50 @@
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
import axios from "axios";
import { pino } from "pino";
import build from "pino-abstract-transport";
import { tryCatch } from "../../globalUtils/tryCatch.js";
const pinoLogLevels: any = {
10: "trace",
20: "debug",
30: "info",
40: "warn",
50: "error",
60: "fatal",
};
export default async function buildGoTransport() {
try {
return build(async function (source) {
for await (let obj of source) {
// Insert log entry into the PostgreSQL database using Drizzle ORM
// convert to the name to make it more easy to find later :P
const levelName = pinoLogLevels[obj.level] || "unknown";
// await db.insert(logs).values({
// level: levelName,
// username: obj?.username.toLowerCase(),
// service: obj?.service.toLowerCase(),
// message: obj.msg,
// });
const { data, error } = (await tryCatch(
axios.post(`${process.env.LST_BASE_URL}/api/v1/log`, {
service: obj?.service.toLowerCase(),
level: levelName,
message: obj.msg,
})
)) as any;
if (error) {
console.log(
"The go server must be offline so we cant post the new logs."
);
}
// console.log(`Go log level: ${levelName}`);
}
});
} catch (err) {
console.error("Error inserting log into database:", err);
}
}

View File

@@ -0,0 +1,62 @@
import { pino, type LogFn, type Logger } from "pino";
export let logLevel = process.env.LOG_LEVEL || "info";
const transport = pino.transport({
targets: [
{
target: "pino-pretty",
options: {
colorize: true,
singleLine: true,
// customPrettifiers: {
// time: (time) => `🕰 ${time}`,
// },
destination: process.stdout.fd,
},
},
{
target: "./dbTransport.js",
},
// Only log to Go if LST_USE_GO=true
...(process.env.LST_USE_GO === "true"
? [
{
target: "./goTransport.js", // New transport for Go
},
]
: []),
],
});
const log: Logger = pino(
{
level: process.env.LOG_LEVEL || logLevel,
//level: "debug",
// formatters: {
// level: (label) => {
// return {level: label.toUpperCase()};
// },
// },
//customLevels: {death: 70},
// removes data from the logs that we dont want to be shown :D
redact: { paths: ["email", "password"], remove: true },
},
transport
);
export const createLog = (
level: "info" | "fatal" | "error" | "warn" | "debug" | "trace",
username: string,
service: string,
message: string
) => {
if (level in log) {
log[level]({ username, service }, message);
} else {
log.warn(
{ username, service },
`Invalid log level '${level}', falling back to warn: ${message}`
);
}
};

View File

@@ -0,0 +1,34 @@
import { OpenAPIHono } from "@hono/zod-openapi";
// routes
import clearLog from "./routes/clearLog.js";
import { db } from "../../../database/dbclient.js";
import { settings } from "../../../database/schema/settings.js";
import { logCleanup } from "./controller/logCleanup.js";
import createNewLog from "./routes/createLog.js";
import getLogs from "./routes/getLogs.js";
import stream from "./routes/streamLogs.js";
const app = new OpenAPIHono();
const routes = [clearLog, createNewLog, getLogs, stream] as const;
//const setting = await db.select().from(settings);
const appRoutes = routes.forEach((route) => {
app.route("/logger", route);
});
app.all("/logger/*", (c) => {
return c.json({
success: false,
message: "You have encounters a log route that dose not exist.",
});
});
// run the clean up job ones on server restart/crash/update and then once a date
logCleanup();
setInterval(async () => {
logCleanup();
}, 60 * 1000 * 60 * 24);
export default app;

View File

@@ -0,0 +1,44 @@
import {createRoute, OpenAPIHono, z} from "@hono/zod-openapi";
import {apiHit} from "../../../globalUtils/apiHits.js";
import {responses} from "../../../globalUtils/routeDefs/responses.js";
import {clearLog} from "../controller/clearLog.js";
const app = new OpenAPIHono({strict: false});
const ParamsSchema = z.object({
id: z
.string()
.min(3)
.openapi({
param: {
name: "id",
in: "path",
},
example: "1212121",
}),
});
app.openapi(
createRoute({
tags: ["server:logger"],
summary: "Marks the select log id as cleared out.",
method: "patch",
path: "/logs/{id}",
request: {
params: ParamsSchema,
},
responses: responses(),
}),
async (c) => {
const {id} = c.req.valid("param");
//const body = await c.req.json();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, {endpoint: `api/logger/logs/id`});
try {
const clear = await clearLog(id);
return c.json({success: clear.success, message: clear.message, data: []}, 200);
} catch (error) {
return c.json({success: false, message: "There was an error clearing the log.", data: error}, 400);
}
}
);
export default app;

View File

@@ -0,0 +1,38 @@
// an external way to creating logs
import {createRoute, OpenAPIHono, z} from "@hono/zod-openapi";
import {apiHit} from "../../../globalUtils/apiHits.js";
import {responses} from "../../../globalUtils/routeDefs/responses.js";
import {createLog} from "../logger.js";
const app = new OpenAPIHono({strict: false});
const CreateLog = z.object({
level: z.string().openapi({example: "info"}),
service: z.string().openapi({example: "server"}),
message: z.string().openapi({example: "This is a new log posted"}),
});
app.openapi(
createRoute({
tags: ["server:logger"],
summary: "Post a log to the db.",
method: "post",
path: "/logs",
description: "This might be a temp soltuin during the transtion between versions",
request: {
body: {content: {"application/json": {schema: CreateLog}}},
},
responses: responses(),
}),
async (c) => {
const body = await c.req.json();
apiHit(c, {endpoint: `api/logger/logs/id`});
try {
createLog(body.level, "logger", body.service, body.message);
return c.json({success: true, message: "A new log was created.", data: []}, 200);
} catch (error) {
return c.json({success: false, message: "There was an error clearing the log.", data: error}, 400);
}
}
);
export default app;

View File

@@ -0,0 +1,39 @@
// an external way to creating logs
import {createRoute, OpenAPIHono, z} from "@hono/zod-openapi";
import {apiHit} from "../../../globalUtils/apiHits.js";
import {responses} from "../../../globalUtils/routeDefs/responses.js";
import {createLog} from "../logger.js";
import {getLogs} from "../controller/getLogs.js";
const app = new OpenAPIHono({strict: false});
const CreateLog = z.object({
level: z.string().openapi({example: "info"}),
service: z.string().openapi({example: "server"}),
message: z.string().openapi({example: "This is a new log posted"}),
});
app.openapi(
createRoute({
tags: ["server:logger"],
summary: "Gets logs.",
method: "get",
path: "/logs",
description: "This might be a temp soltuin during the transtion between versions",
request: {
body: {content: {"application/json": {schema: CreateLog}}},
},
responses: responses(),
}),
async (c) => {
const query = await c.req.queries();
apiHit(c, {endpoint: `api/logger/logs`});
try {
const logData = await getLogs(query);
return c.json({success: logData?.success, message: logData?.message, data: logData?.data}, 200);
} catch (error) {
return c.json({success: false, message: "There was an error clearing the log.", data: error}, 400);
}
}
);
export default app;

View File

@@ -0,0 +1,51 @@
// an external way to creating logs
//@ts-nocheck
import {createRoute, OpenAPIHono, z} from "@hono/zod-openapi";
import {apiHit} from "../../../globalUtils/apiHits.js";
import {responses} from "../../../globalUtils/routeDefs/responses.js";
import {createLog} from "../logger.js";
import {getLogs} from "../controller/getLogs.js";
import {streamLogs} from "../controller/streamLogs.js";
import {streamSSE} from "hono/streaming";
const app = new OpenAPIHono({strict: false});
app.openapi(
createRoute({
tags: ["server:logger"],
summary: "Streams the logs to the frontend.",
method: "get",
path: "/logs/stream",
description: "This should only be used on the event you need to monitor logs.",
responses: {
200: {
content: {
"application/json": {schema: z.object({message: z.string().optional()})},
},
description: "Response message",
},
},
}),
async (c) => {
apiHit(c, {endpoint: `api/logger/logs`});
c.header("Content-Type", "text/event-stream");
c.header("Cache-Control", "no-cache");
c.header("Connection", "keep-alive");
return streamSSE(c, async (stream) => {
let id = 0;
const encoder = new TextEncoder();
while (true) {
const message = `It is ${new Date().toISOString()}`;
await stream.writeSSE({
data: message,
event: "time-update",
id: String(id++),
});
encoder.encode(`data: ${JSON.stringify({type: "progress", data: id})}\n\n`);
await stream.sleep(1000);
}
});
}
);
export default app;

View File

@@ -0,0 +1,120 @@
import axios from "axios";
import { commandLog } from "../../../../../database/schema/commandLog.js";
import { prodEndpointCreation } from "../../../../globalUtils/createUrl.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { lstAuth } from "../../../../index.js";
import { createSSCC } from "../../../../globalUtils/createSSCC.js";
import { db } from "../../../../../database/dbclient.js";
import net from "net";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { labelInfo } from "../../../sqlServer/querys/warehouse/labelInfo.js";
import { settings } from "../../../../../database/schema/settings.js";
import { eq } from "drizzle-orm";
import { serverData } from "../../../../../database/schema/serverData.js";
export const removeAsNonReusable = async (data: any) => {
// const removalUrl = await prodEndpointCreation(
// "/public/v1.0/Warehousing/RemoveAsNonReusableMaterial"
// );
// const sscc = await createSSCC(data.runningNr);
// const { data: remove, error } = await tryCatch(
// axios.post(
// removalUrl,
// { scannerId: "500", sscc: sscc.slice(2) },
// {
// headers: { Authorization: `Basic ${lstAuth}` },
// }
// )
// );
// use a scanner tcp connection to trigger this process
const STX = "\x02";
const ETX = "\x03";
const scanner = new net.Socket();
let stage = 0;
// get the label info
const { data: label, error: labelError } = (await tryCatch(
query(labelInfo.replaceAll("[runningNr]", data.runningNr), "Label Info")
)) as any;
if (label.data[0].stockStatus === "notOnStock") {
return {
success: false,
message: `The label: ${data.runningNr} is not currently in stock`,
data: [],
};
}
// get the server ip based on the token.
const setting = await db.select().from(settings);
const plantInfo = await db.select().from(serverData);
const plantToken = setting.filter((n: any) => n.name === "plantToken");
const scannerID = setting.filter((n: any) => n.name === "scannerID");
const scannerPort = setting.filter((n: any) => n.name === "scannerPort");
const plantData = plantInfo.filter(
(p: any) => p.plantToken === plantToken[0].value
);
scanner.connect(
parseInt(scannerPort[0].value),
plantData[0].idAddress!,
async () => {
// need to get the ip from the server data and scanner port
//console.log(`connected to scanner`);
scanner.write(`${STX}${scannerID[0].value}@AlplaPRODcmd23${ETX}`);
}
);
scanner.on("data", (data) => {
const response = data.toString();
//console.log("Received:", response.trimStart());
if (stage === 0) {
stage = 1;
scanner.write(
`${STX}${scannerID[0].value}@${label.data[0].Barcode}${ETX}`
);
} else if (stage === 1) {
scanner.end();
}
});
scanner.on("close", () => {
//console.log("Connection closed");
scanner.destroy();
});
scanner.on("error", (err) => {
//console.error("Scanner error:", err);
scanner.destroy();
return {
success: false,
message: `The label: ${data.runningNr} encountering an error while being removed, please try again`,
data: [],
};
});
// if (error) {
// //console.log(error);
// return {
// success: false,
// message: `There was an error removing ${data.runningNr}`,
// data: [],
// };
// }
let reason = data.reason || "";
delete data.reason;
const { data: commandL, error: ce } = await tryCatch(
db.insert(commandLog).values({
commandUsed: "removeAsNonReusable",
bodySent: data,
reasonUsed: reason,
})
);
return {
success: true,
message: `The label: ${data.runningNr}, was removed`,
data: [],
};
};

View File

@@ -0,0 +1,34 @@
import * as XLSX from "xlsx";
export const standardForCastTemplate = async () => {
/**
* Creates the standard Template for bulk orders in
*/
const headers = [
["CustomerArticleNumber", "Quantity", "RequirementDate", "CustomerID"],
];
// create a new workbook
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.aoa_to_sheet(headers);
//const ws2 = XLSX.utils.aoa_to_sheet(headers2);
const columnWidths = headers[0].map((header) => ({
width: header.length + 2,
}));
ws["!cols"] = columnWidths;
// append the worksheet to the workbook
XLSX.utils.book_append_sheet(wb, ws, `Sheet1`);
//XLSX.utils.book_append_sheet(wb, ws2, `Sheet2`);
// Write the excel file and trigger the download'
XLSX.writeFile(wb, "BulkForecastTemplate");
// Write the workbook to a buffer and return it
const excelBuffer = XLSX.write(wb, { bookType: "xlsx", type: "buffer" });
return excelBuffer;
};

View File

@@ -0,0 +1,48 @@
import { lorealForecast } from "./mappings/loralForecast.js";
import { pNgForecast } from "./mappings/pNgForecast.js";
import { standardForecast } from "./mappings/standardForcast.js";
export const forecastIn = async (data: any, user: any) => {
/**
* Bulk orders in, and custom file parsing.
*/
let success = true;
let message = "";
let orderData: any = [];
// what type of order are we dealing with?
if (data["fileType"] === "standard") {
//run the standard forecast in
const standard = await standardForecast(data["postForecast"], user);
success = standard.success ?? false;
message = standard.message ?? "Error posting standard forecast";
orderData = standard.data;
}
if (data["fileType"] === "energizer") {
// orders in
}
if (data["fileType"] === "loreal") {
//run the standard forecast in
const loreal = await lorealForecast(data["postForecast"], user);
success = loreal.success ?? false;
message = loreal.message ?? "Error posting standard forecast";
orderData = loreal.data;
}
if (data["fileType"] === "pg") {
//run the standard forecast in
const pg = await pNgForecast(data["postForecast"], user);
success = pg.success ?? false;
message = pg.message ?? "Error posting standard forecast";
orderData = pg.data;
}
return {
success,
message,
data: orderData,
};
};

View File

@@ -0,0 +1,286 @@
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import XLSX from "xlsx";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { postForecast } from "../postForecast.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js";
import { addDays } from "date-fns";
import { sendEmail } from "../../../../../notifications/controller/sendMail.js";
import { createLog } from "../../../../../logger/logger.js";
let customerID = 4;
export const lorealForecast = async (data: any, user: any) => {
/**
* Post a standard forecast based on the standard template.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheet: any = workbook.Sheets["Alpla HDPE"];
const range = XLSX.utils.decode_range(sheet["!ref"]);
const psheet: any = workbook.Sheets["Alpla PET"];
const prange = XLSX.utils.decode_range(psheet["!ref"]);
const headers = [];
for (let C = range.s.c; C <= range.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: 1, c: C }); // row 0 = Excel row 1
const cell = sheet[cellAddress];
headers.push(cell ? cell.v : undefined);
}
const pheaders = [];
for (let C = prange.s.c; C <= prange.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: 1, c: C }); // row 0 = Excel row 1
const cell = psheet[cellAddress];
pheaders.push(cell ? cell.v : undefined);
}
const ebmForeCastData: any = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 3,
});
const petForeCastData: any = XLSX.utils.sheet_to_json(psheet, {
defval: "",
header: pheaders,
range: 3,
});
const ebmForecastData: any = [];
const missingSku: any = [];
const { data: a, error: ae } = await tryCatch(
query(activeArticle, "Loreal calling active av")
);
if (ae) {
return {
success: false,
message: "Error getting active av",
data: [],
};
}
const article: any = a?.data;
// process the ebm forcast
for (let i = 0; i < ebmForeCastData.length; i++) {
// bottle code
const sku = ebmForeCastData[i]["HDPE Bottle Code"];
// ignore the blanks
if (sku === "") continue;
// ignore zero qty
// if (ebmForeCastData[i][`Day ${i}`]) continue;
for (let f = 0; f <= 90; f++) {
const day = `Day ${f + 1}`;
// if (ebmForeCastData[i][day] === 0) continue;
const forcast = {
customerArticleNo: sku,
requirementDate: addDays(new Date(Date.now()), f), //excelDateStuff(parseInt(date)),
quantity: ebmForeCastData[i][day] ?? 0,
};
if (forcast.quantity === 0) continue;
// checking to make sure there is a real av to add to.
const activeAV = article.filter(
(c: any) =>
c?.CustomerArticleNumber ===
forcast.customerArticleNo.toString()
);
if (activeAV.length === 0) {
if (typeof forcast.customerArticleNo === "number") {
missingSku.push(forcast);
}
continue;
}
ebmForecastData.push(forcast);
}
//console.log(ebmForeCastData.length);
}
// petForeCastData.forEach((item: any, index: any) => {
// //console.log(`Processing item ${index + 1} of ${forecastData.length}`);
// // Extract the customer code
// const customerCode = item["SOUTH PET BOTTLES"];
// // Process each date in the current object
// for (const [date, qty] of Object.entries(item)) {
// // Skip metadata fields
// if (petMetadataFields.includes(date)) continue;
// if (qty === 0) continue;
// // Create your transformed record
// const record = {
// customerArticleNo: customerCode,
// requirementDate: excelDateStuff(parseInt(date)),
// quantity: qty,
// };
// // Do something with this record
// petForecastData.push(record);
// }
// });
// pet forecast
for (let i = 0; i < petForeCastData.length; i++) {
// bottle code
const sku = petForeCastData[i]["South PET Bottle Code"];
// ignore the blanks
if (sku === "") continue;
// ignore zero qty
// if (ebmForeCastData[i][`Day ${i}`]) continue;
for (let f = 0; f <= 90; f++) {
const day = `Day ${f + 1}`;
// if (ebmForeCastData[i][day] === 0) continue;
const forcast = {
customerArticleNo: sku,
requirementDate: addDays(new Date(Date.now()), f), //excelDateStuff(parseInt(date)),
quantity: petForeCastData[i][day] ?? 0,
};
if (forcast.quantity === 0 || forcast.quantity === "") continue;
if (forcast.customerArticleNo < 99999) {
//console.log(`Sku a normal av ${forcast.customerArticleNo}`);
continue;
}
// checking to make sure there is a real av to add to.
const activeAV = article.filter(
(c: any) =>
c?.CustomerArticleNumber ===
forcast.customerArticleNo.toString()
);
if (activeAV.length === 0) {
if (typeof forcast.customerArticleNo === "number") {
missingSku.push(forcast);
}
continue;
}
ebmForecastData.push(forcast);
}
}
//console.log(comForecast);
// email the for the missing ones
const missedGrouped = Object.values(
missingSku.reduce((acc: any, item: any) => {
const key = item.customerArticleNo;
if (!acc[key]) {
// first time we see this customer
acc[key] = item;
} else {
// compare dates and keep the earliest
if (
new Date(item.requirementDate) <
new Date(acc[key].requirementDate)
) {
acc[key] = item;
}
}
return acc;
}, {})
);
const emailSetup = {
email: "Blake.matthes@alpla.com; Stuart.Gladney@alpla.com; Harold.Mccalister@alpla.com; Jenn.Osbourn@alpla.com",
subject:
missedGrouped.length > 0
? `Alert! There are ${missedGrouped.length}, missing skus.`
: `Alert! There is a missing SKU.`,
template: "missingLorealSkus",
context: {
items: missedGrouped,
},
};
const { data: sentEmail, error: sendEmailError } = await tryCatch(
sendEmail(emailSetup)
);
if (sendEmailError) {
createLog(
"error",
"blocking",
"notify",
"Failed to send email, will try again on next interval"
);
return {
success: false,
message: "Failed to send email, will try again on next interval",
};
}
// if the customerarticle number is not matching just ignore it
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `ForecastFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
sender: user.username || "lst-system",
customerId: customerID,
positions: [],
};
let updatedPredefinedObject = {
...predefinedObject,
positions: [
...predefinedObject.positions,
...ebmForecastData,
// ...ebmForecastData.filter(
// (q: any) =>
// q.customerArticleNo != "" && q.customerArticleNo != "Total"
// ),
// ...petForecastData.filter(
// (q: any) =>
// q.customerArticleNo != "" && q.customerArticleNo != "Total"
// ),
],
};
// console.log(updatedPredefinedObject);
const posting: any = await postForecast(updatedPredefinedObject, user);
return {
success: posting.success,
message: posting.message,
data: posting.data === "" ? ebmForecastData : posting.data,
};
};

View File

@@ -0,0 +1,164 @@
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import XLSX from "xlsx";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { postForecast } from "../postForecast.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js";
export const pNgForecast = async (data: any, user: any) => {
/**
* Post a standard forecast based on the standard template.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
const pNg = s.filter((n: any) => n.name === "pNgAddress");
const { data: a, error: ae } = await tryCatch(
query(activeArticle, "p&g active av")
);
if (ae) {
return {
success: false,
message: "Error getting active av",
data: [],
};
}
const article: any = a?.data;
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0];
//const sheet: any = workbook.Sheets[sheetName];
const sheet: any = workbook.Sheets["SchedAgreementUIConfigSpreadshe"];
const range = XLSX.utils.decode_range(sheet["!ref"]);
const headers = [];
for (let C = range.s.c; C <= range.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: 0, c: C }); // row 0 = Excel row 1
const cell = sheet[cellAddress];
headers.push(cell ? cell.v : undefined);
}
//console.log(headers);
const forecastData: any = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 1,
});
const groupedByCustomer: any = forecastData.reduce(
(acc: any, item: any) => {
const id = item.CustomerID;
if (!acc[id]) {
acc[id] = [];
}
acc[id].push(item);
return acc;
},
{}
);
const foreCastData: any = [];
for (const [customerID, forecast] of Object.entries(groupedByCustomer)) {
//console.log(`Running for Customer ID: ${customerID}`);
const newForecast: any = forecast;
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `ForecastFromLST-${new Date(
Date.now()
).toLocaleString("en-US")}`,
sender: user.username || "lst-system",
customerId: pNg[0].value,
positions: [],
};
// map everything out for each order
const nForecast = newForecast.map((o: any) => {
// const invoice = i.filter(
// (i: any) => i.deliveryAddress === parseInt(customerID)
// );
// if (!invoice) {
// return;
// }
return {
customerArticleNo: parseInt(o["Customer Item No."]),
requirementDate: excelDateStuff(parseInt(o["Request Date"])),
quantity: o["Remaining Qty to be Shipped"],
};
});
// check to make sure the av belongs in this plant.
const onlyNumbers = nForecast.filter((n: any) => n.quantity > 0);
const filteredForecast: any = [];
for (let i = 0; i < nForecast.length; i++) {
//console.log(nForecast[i].customerArticleNo);
const activeAV = article.filter(
(c: any) =>
c?.CustomerArticleNumber ===
nForecast[i]?.customerArticleNo.toString() &&
// validate it works via the default address
c?.IdAdresse === parseInt(pNg[0].value)
);
if (activeAV.length > 0) {
filteredForecast.push(onlyNumbers[i]);
}
}
if (filteredForecast.length === 0) {
console.log("Nothing to post");
return {
success: true,
message: "No forecast to be posted",
data: foreCastData,
};
}
// do that fun combining thing
let updatedPredefinedObject = {
...predefinedObject,
positions: [...predefinedObject.positions, ...filteredForecast],
};
//console.log(updatedPredefinedObject);
// post the orders to the server
const posting: any = await postForecast(updatedPredefinedObject, user);
foreCastData.push({
customer: customerID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
});
}
return {
success: true,
message: "Forecast Posted",
data: foreCastData,
};
};

View File

@@ -0,0 +1,114 @@
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import XLSX from "xlsx";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { postForecast } from "../postForecast.js";
export const standardForecast = async (data: any, user: any) => {
/**
* Post a standard forecast based on the standard template.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0];
const sheet = workbook.Sheets[sheetName];
const headers = [
"CustomerArticleNumber",
"Quantity",
"RequirementDate",
"CustomerID",
];
const forecastData: any = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 1,
});
const groupedByCustomer: any = forecastData.reduce(
(acc: any, item: any) => {
const id = item.CustomerID;
if (!acc[id]) {
acc[id] = [];
}
acc[id].push(item);
return acc;
},
{}
);
const foreCastData: any = [];
for (const [customerID, forecast] of Object.entries(groupedByCustomer)) {
//console.log(`Running for Customer ID: ${customerID}`);
const newForecast: any = forecast;
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `ForecastFromLST-${new Date(
Date.now()
).toLocaleString("en-US")}`,
sender: user.username || "lst-system",
customerId: customerID,
positions: [],
};
// map everything out for each order
const nForecast = newForecast.map((o: any) => {
// const invoice = i.filter(
// (i: any) => i.deliveryAddress === parseInt(customerID)
// );
// if (!invoice) {
// return;
// }
return {
customerArticleNo: o.CustomerArticleNumber,
requirementDate: excelDateStuff(parseInt(o.RequirementDate)),
quantity: o.Quantity,
};
});
// do that fun combining thing
let updatedPredefinedObject = {
...predefinedObject,
positions: [...predefinedObject.positions, ...nForecast],
};
//console.log(updatedPredefinedObject);
// post the orders to the server
const posting: any = await postForecast(updatedPredefinedObject, user);
foreCastData.push({
customer: customerID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
});
}
return {
success: true,
message: "Forecast Posted",
data: foreCastData,
};
};

View File

@@ -0,0 +1,76 @@
import axios from "axios";
import { prodEndpointCreation } from "../../../../../globalUtils/createUrl.js";
import { createLog } from "../../../../logger/logger.js";
export const postForecast = async (data: any, user: any) => {
let endpoint = await prodEndpointCreation(
"/public/v1.0/DemandManagement/DELFOR"
);
//console.log(endpoint);
//console.log(req.body.orders[0]);
try {
const results = await axios({
url: endpoint,
method: "POST",
headers: {
"X-API-Key": process.env.TEC_API_KEY || "",
"Content-Type": "application/json",
},
// if a body is sent over it would be like below
data: data,
});
//console.log(results.data);
//console.log(results.status);
if (results.data.errors) {
createLog(
"error",
"forecast",
"logistics",
`There was an error posting the Forecast: ${JSON.stringify(
results.data.errors
)}`
);
return {
success: true,
message: "Error processing forecast",
data: results.data.errors[0].message,
};
}
if (results.status === 200) {
createLog(
"info",
"forecast",
"logistics",
`Forcast was successfully posted: ${JSON.stringify(
results.data
)}`
);
return {
success: true,
message: "Success on posting forecast",
data: results.data,
};
}
} catch (error: any) {
//console.log(`There is an error`, error);
if (error) {
//console.log(error.response.data);
createLog(
"error",
"forecast",
"logistics",
`There was an error posting the Forecast: ${JSON.stringify(
error.response.data
)}`
);
return {
success: false,
message: "There was an error posting the Forecast",
data: error.response.data,
};
}
}
};

View File

@@ -0,0 +1,44 @@
import * as XLSX from "xlsx";
export const standardTemplate = async () => {
/**
* Creates the standard Template for bulk orders in
*/
const headers = [
[
"CustomerArticleNumber",
"CustomerOrderNumber",
"CustomerLineNumber",
"CustomerRealeaseNumber",
"Quantity",
"DeliveryDate",
"CustomerID",
"Remark",
// "InvoiceID",
],
];
// create a new workbook
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.aoa_to_sheet(headers);
//const ws2 = XLSX.utils.aoa_to_sheet(headers2);
const columnWidths = headers[0].map((header) => ({
width: header.length + 2,
}));
ws["!cols"] = columnWidths;
// append the worksheet to the workbook
XLSX.utils.book_append_sheet(wb, ws, `Sheet1`);
//XLSX.utils.book_append_sheet(wb, ws2, `Sheet2`);
// Write the excel file and trigger the download'
XLSX.writeFile(wb, "BulkOrdersTemplate");
// Write the workbook to a buffer and return it
const excelBuffer = XLSX.write(wb, { bookType: "xlsx", type: "buffer" });
return excelBuffer;
};

View File

@@ -0,0 +1,184 @@
import XLSX from "xlsx";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { bulkOrderArticleInfo } from "../../../../../sqlServer/querys/dm/bulkOrderArticleInfo.js";
import { addDays, addHours, isAfter, parse } from "date-fns";
import { orderState } from "../../../../../sqlServer/querys/dm/orderState.js";
import { postOrders } from "../postOrders.js";
// customeris/articles stuff will be in basis once we move to iowa
let customerID = 8;
let invoiceID = 9;
let articles = "118,120";
export const abbottOrders = async (data: any, user: any) => {
/**
* Standard orders meaning that we get the standard file exported and fill it out and uplaod to lst.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
// articleInfo
const { data: article, error: ae } = await tryCatch(
query(
bulkOrderArticleInfo.replace("[articles]", articles),
"Get Article data for bulk orders"
)
);
const a: any = article?.data;
if (ae) {
return {
sucess: false,
message: `Error getting article data`,
data: ae,
};
}
// order state
const { data: o, error: oe } = await tryCatch(
query(orderState, "Gets the next 500 orders that have not been started")
);
const openOrders: any = o?.data;
if (oe) {
return {
sucess: false,
message: `Error getting article data`,
data: oe,
};
}
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0];
const sheet = workbook.Sheets[sheetName];
// Define custom headers
const customHeaders = ["date", "time", "newton8oz", "newton10oz"];
const orderData = XLSX.utils.sheet_to_json(sheet, {
range: 5, // Start at row 5 (index 4)
header: customHeaders,
defval: "", // Default value for empty cells
});
// the base of the import
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
sender: user.username || "lst-system",
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
orders: [],
};
const oOrders: any = openOrders;
let correctedOrders: any = orderData
.filter(
(o: any) =>
(o.newton8oz && o.newton8oz.trim() !== "") ||
(o.newton10oz && o.newton10oz.trim() !== "")
)
.map((o: any) => ({
date: excelDateStuff(o.date, o.time),
po:
o.newton8oz.replace(/\s+/g, "") !== ""
? o.newton8oz.replace(/\s+/g, "")
: o.newton10oz.replace(/\s+/g, ""),
customerArticlenumber:
o.newton8oz != ""
? a.filter((a: any) => a.av === 118)[0]
.CustomerArticleNumber
: a.filter((a: any) => a.av === 120)[0]
.CustomerArticleNumber,
qty:
o.newton8oz != ""
? a.filter((a: any) => a.av === 118)[0].totalTruckLoad
: a.filter((a: any) => a.av === 120)[0].totalTruckLoad,
}));
// now we want to make sure we only correct orders that or after now
correctedOrders = correctedOrders.filter((o: any) => {
const parsedDate = parse(o.date, "M/d/yyyy, h:mm:ss a", new Date());
return isAfter(o.date, new Date().toISOString());
});
// last map to remove orders that have already been started
// correctedOrders = correctedOrders.filter((oo: any) =>
// oOrders.some((o: any) => o.CustomerOrderNumber === oo.po)
// );
let postedOrders: any = [];
const filterOrders: any = correctedOrders;
filterOrders.forEach((oo: any) => {
const isMatch = openOrders.some(
(o: any) => String(o.po).trim() === String(oo.po).trim()
);
if (!isMatch) {
//console.log(`ok to update: ${oo.po}`);
// oo = {
// ...oo,
// CustomerOrderNumber: oo.CustomerOrderNumber.replace(" ", ""),
// };
postedOrders.push(oo);
} else {
// console.log(`Not valid order to update: ${oo.po}`);
//console.log(oo)
}
});
// Map Excel data to predefinedObject format
const orders = filterOrders.map((o: any) => {
return {
customerId: customerID,
invoiceAddressId: invoiceID,
customerOrderNo: o.po,
orderDate: new Date(Date.now()).toLocaleString("en-US"),
positions: [
{
deliveryAddressId: 8,
customerArticleNo: o.customerArticlenumber,
quantity: o.qty,
deliveryDate: addHours(addDays(o.date, 1), 1), // adding this in so we can over come the constant 1 day behind thing as a work around
customerLineItemNo: 1, // this is how it is currently sent over from abbott
customerReleaseNo: 1, // same as above
},
],
};
});
// combine it all together.
const updatedPredefinedObject = {
...predefinedObject,
orders: [...predefinedObject.orders, ...orders],
};
//console.log(updatedPredefinedObject);
// post the orders to the server
const posting = await postOrders(updatedPredefinedObject, user);
//console.log(posting);
return {
success: posting?.success,
message: posting?.message,
data: posting,
};
};

View File

@@ -0,0 +1,172 @@
import XLSX from "xlsx";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { orderState } from "../../../../../sqlServer/querys/dm/orderState.js";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { invoiceAddress } from "../../../../../sqlServer/querys/dm/invoiceAddress.js";
import { postOrders } from "../postOrders.js";
export const energizerOrders = async (data: any, user: any) => {
/**
* Standard orders meaning that we get the standard file exported and fill it out and uplaod to lst.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
// order state
const { data: o, error: oe } = await tryCatch(
query(orderState, "Gets the next 500 orders that have not been started")
);
const openOrders: any = o?.data;
if (oe) {
return {
sucess: false,
message: `Error getting article data`,
data: oe,
};
}
// order state
const { data: invoice, error: ie } = await tryCatch(
query(invoiceAddress, "Gets invoices addresses")
);
const i: any = invoice?.data;
if (ie) {
return {
sucess: false,
message: `Error getting invoice address data`,
data: ie,
};
}
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0];
const sheet = workbook.Sheets[sheetName];
// define custom headers
const headers = [
"ITEM",
"PO",
"ReleaseNo",
"QTY",
"DELDATE",
"COMMENTS",
"What changed",
"CUSTOMERID",
"Remark",
];
const orderData = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 1,
});
// the base of the import
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
sender: user.username || "lst-system",
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
orders: [],
};
let newOrders: any = orderData;
// filter out the orders that have already been started just to reduce the risk of errors.
newOrders.filter((oo: any) =>
openOrders.some(
(o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber
)
);
// filter out the blanks
newOrders = newOrders.filter((z: any) => z.ITEM !== "");
// let postedOrders: any = [];
// for (const [customerID, orders] of Object.entries(orderData)) {
// // console.log(`Running for Customer ID: ${customerID}`);
// const newOrders: any = orderData;
// // filter out the orders that have already been started just to reduce the risk of errors.
// newOrders.filter((oo: any) =>
// openOrders.some(
// (o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber
// )
// );
// // map everything out for each order
const nOrder = newOrders.map((o: any) => {
const invoice = i.filter(
(i: any) => i.deliveryAddress === parseInt(o.CUSTOMERID)
);
if (!invoice) {
return;
}
return {
customerId: parseInt(o.CUSTOMERID),
invoiceAddressId: invoice[0].invoiceAddress, // matched to the default invoice address
customerOrderNo: o.PO,
orderDate: new Date(Date.now()).toLocaleString("en-US"),
positions: [
{
deliveryAddressId: parseInt(o.CUSTOMERID),
customerArticleNo: o.ITEM,
quantity: parseInt(o.QTY),
deliveryDate: o.DELDATE, //excelDateStuff(o.DELDATE),
customerLineItemNo: o.ReleaseNo, // this is how it is currently sent over from abbott
customerReleaseNo: o.ReleaseNo, // same as above
remark: o.remark === "" ? null : o.remark,
},
],
};
});
// // do that fun combining thing
const updatedPredefinedObject = {
...predefinedObject,
orders: [...predefinedObject.orders, ...nOrder],
};
// //console.log(updatedPredefinedObject);
// // post the orders to the server
const posting: any = await postOrders(updatedPredefinedObject, user);
return {
customer: nOrder[0].CUSTOMERID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
};
// }
// return {
// success: true,
// message:
// "Standard Template was just processed successfully, please check AlplaProd 2.0 to confirm no errors. ",
// data: nOrder,
// };
};

View File

@@ -0,0 +1,200 @@
import { delay } from "../../../../../../globalUtils/delay.js";
import XLSX from "xlsx";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { orderState } from "../../../../../sqlServer/querys/dm/orderState.js";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { invoiceAddress } from "../../../../../sqlServer/querys/dm/invoiceAddress.js";
import { postOrders } from "../postOrders.js";
export const macroImportOrders = async (data: any, user: any) => {
/**
* Standard orders meaning that we get the standard file exported and fill it out and uplaod to lst.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
// order state
const { data: o, error: oe } = await tryCatch(
query(orderState, "Gets the next 500 orders that have not been started")
);
const openOrders: any = o?.data;
if (oe) {
return {
sucess: false,
message: `Error getting article data`,
data: oe,
};
}
// order state
const { data: invoice, error: ie } = await tryCatch(
query(invoiceAddress, "Gets invoices addresses")
);
const i: any = invoice?.data;
if (ie) {
return {
sucess: false,
message: `Error getting invoice address data`,
data: ie,
};
}
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
//const sheetName = workbook.SheetNames[0];
const sheet = workbook.Sheets["Data"];
// define custom headers
const headers = [
"CustomerArticleNumber",
"CustomerOrderNumber",
"CustomerLineNumber",
"CustomerRealeaseNumber",
"Quantity",
"DeliveryDate",
"CustomerID",
"Remark",
];
const orderData = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 5,
});
// the base of the import
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
sender: user.username || "lst-system",
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
orders: [],
};
const removeBlanks = orderData.filter(
(n: any) => n.CustomerArticleNumber != ""
);
console.log(removeBlanks);
const groupedByCustomer: any = removeBlanks.reduce(
(acc: any, item: any) => {
const id = item.CustomerID;
if (!acc[id]) {
acc[id] = [];
}
acc[id].push(item);
return acc;
},
{}
);
let postedOrders: any = [];
for (const [customerID, orders] of Object.entries(groupedByCustomer)) {
// console.log(`Running for Customer ID: ${customerID}`);
const filterOrders: any = orders;
const newOrders: any = [];
//newOrders.filter((oo) => openOrders.some((o) => String(o.CustomerOrderNumber) === String(oo.CustomerOrderNumber)));
//console.log(newOrders)
filterOrders.forEach((oo: any) => {
const isMatch = openOrders.some(
(o: any) =>
// check the header
String(o.CustomerOrderNumber).trim() ===
String(oo.CustomerOrderNumber).trim() &&
// and check the customer release is not in here.
String(o.CustomerRealeaseNumber).trim() ===
String(oo.CustomerRealeaseNumber).trim()
);
if (!isMatch) {
console.log(`ok to update: ${oo.CustomerOrderNumber}`);
newOrders.push(oo);
} else {
console.log(
`Not valid order to update: ${oo.CustomerOrderNumber}`
);
//console.log(oo)
}
});
// filter out the orders that have already been started just to reduce the risk of errors.
newOrders.filter((oo: any) =>
openOrders.some(
(o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber
)
);
// map everything out for each order
const nOrder = newOrders.map((o: any) => {
const invoice = i.filter(
(i: any) => i.deliveryAddress === parseInt(customerID)
);
if (!invoice) {
return;
}
return {
customerId: parseInt(customerID),
invoiceAddressId: invoice[0]?.invoiceAddress, // matched to the default invoice address
customerOrderNo: o.CustomerOrderNumber,
orderDate: new Date(Date.now()).toLocaleString("en-US"),
positions: [
{
deliveryAddressId: parseInt(customerID),
customerArticleNo: o.CustomerArticleNumber,
quantity: parseInt(o.Quantity),
deliveryDate: excelDateStuff(o.DeliveryDate),
customerLineItemNo: o.CustomerLineNumber, // this is how it is currently sent over from abbott
customerReleaseNo: o.CustomerRealeaseNumber, // same as above
remark: o.remark === "" ? null : o.remark,
},
],
};
});
// do that fun combining thing
const updatedPredefinedObject = {
...predefinedObject,
orders: [...predefinedObject.orders, ...nOrder],
};
//console.log(updatedPredefinedObject);
// post the orders to the server
const posting: any = await postOrders(updatedPredefinedObject, user);
postedOrders.push({
customer: customerID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
});
}
return {
success: true,
message:
"Standard Template was just processed successfully, please check AlplaProd 2.0 to confirm no errors. ",
data: postedOrders,
};
};

View File

@@ -0,0 +1,192 @@
import { delay } from "../../../../../../globalUtils/delay.js";
import XLSX from "xlsx";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { orderState } from "../../../../../sqlServer/querys/dm/orderState.js";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { invoiceAddress } from "../../../../../sqlServer/querys/dm/invoiceAddress.js";
import { postOrders } from "../postOrders.js";
export const standardOrders = async (data: any, user: any) => {
/**
* Standard orders meaning that we get the standard file exported and fill it out and uplaod to lst.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
// order state
const { data: o, error: oe } = await tryCatch(
query(orderState, "Gets the next 500 orders that have not been started")
);
const openOrders: any = o?.data;
if (oe) {
return {
sucess: false,
message: `Error getting article data`,
data: oe,
};
}
// order state
const { data: invoice, error: ie } = await tryCatch(
query(invoiceAddress, "Gets invoices addresses")
);
const i: any = invoice?.data;
if (ie) {
return {
sucess: false,
message: `Error getting invoice address data`,
data: ie,
};
}
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0];
const sheet = workbook.Sheets[sheetName];
// define custom headers
const headers = [
"CustomerArticleNumber",
"CustomerOrderNumber",
"CustomerLineNumber",
"CustomerRealeaseNumber",
"Quantity",
"DeliveryDate",
"CustomerID",
"Remark",
];
const orderData = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 1,
});
// the base of the import
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
sender: user.username || "lst-system",
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
orders: [],
};
const groupedByCustomer: any = orderData.reduce((acc: any, item: any) => {
const id = item.CustomerID;
if (!acc[id]) {
acc[id] = [];
}
acc[id].push(item);
return acc;
}, {});
let postedOrders: any = [];
for (const [customerID, orders] of Object.entries(groupedByCustomer)) {
// console.log(`Running for Customer ID: ${customerID}`);
const filterOrders: any = orders;
const newOrders: any = [];
//newOrders.filter((oo) => openOrders.some((o) => String(o.CustomerOrderNumber) === String(oo.CustomerOrderNumber)));
//console.log(newOrders)
filterOrders.forEach((oo: any) => {
const isMatch = openOrders.some(
(o: any) =>
// check the header
String(o.CustomerOrderNumber).trim() ===
String(oo.CustomerOrderNumber).trim() &&
// and check the customer release is not in here.
String(o.CustomerRealeaseNumber).trim() ===
String(oo.CustomerRealeaseNumber).trim()
);
if (!isMatch) {
console.log(`ok to update: ${oo.CustomerOrderNumber}`);
newOrders.push(oo);
} else {
console.log(
`Not valid order to update: ${oo.CustomerOrderNumber}`
);
//console.log(oo)
}
});
// filter out the orders that have already been started just to reduce the risk of errors.
newOrders.filter((oo: any) =>
openOrders.some(
(o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber
)
);
// map everything out for each order
const nOrder = newOrders.map((o: any) => {
const invoice = i.filter(
(i: any) => i.deliveryAddress === parseInt(customerID)
);
if (!invoice) {
return;
}
return {
customerId: parseInt(customerID),
invoiceAddressId: invoice[0]?.invoiceAddress, // matched to the default invoice address
customerOrderNo: o.CustomerOrderNumber,
orderDate: new Date(Date.now()).toLocaleString("en-US"),
positions: [
{
deliveryAddressId: parseInt(customerID),
customerArticleNo: o.CustomerArticleNumber,
quantity: parseInt(o.Quantity),
deliveryDate: excelDateStuff(o.DeliveryDate),
customerLineItemNo: o.CustomerLineNumber, // this is how it is currently sent over from abbott
customerReleaseNo: o.CustomerRealeaseNumber, // same as above
remark: o.Remark === "" ? null : o.Remark,
},
],
};
});
// do that fun combining thing
const updatedPredefinedObject = {
...predefinedObject,
orders: [...predefinedObject.orders, ...nOrder],
};
//console.log(updatedPredefinedObject.orders[0]);
// post the orders to the server
const posting: any = await postOrders(updatedPredefinedObject, user);
postedOrders.push({
customer: customerID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
});
}
return {
success: true,
message:
"Standard Template was just processed successfully, please check AlplaProd 2.0 to confirm no errors. ",
data: postedOrders,
};
};

View File

@@ -0,0 +1,61 @@
import { abbottOrders } from "./mappings/abbottTruckList.js";
import { energizerOrders } from "./mappings/energizerOrdersIn.js";
import { macroImportOrders } from "./mappings/macroImport.js";
import { standardOrders } from "./mappings/standardOrders.js";
export const ordersIn = async (data: any, user: any) => {
/**
* Bulk orders in, and custom file parsing.
*/
let success = true;
let message = "";
let orderData: any = [];
// what type of order are we dealing with?
if (data["fileType"] === "standard") {
// run the standard orders in
const standard = await standardOrders(data["postOrders"], user);
success = standard.success ?? false;
message = standard.message ?? "Error posting Standard Orders";
orderData = standard.data;
}
if (data["fileType"] === "abbott") {
// orders in
const abbott = await abbottOrders(data["postOrders"], user);
success = abbott.success ?? false;
message = abbott.message ?? "Error posting Abbott Orders";
orderData = abbott.data;
}
if (data["fileType"] === "energizer") {
// orders in
const energizer = await energizerOrders(data["postOrders"], user);
success = energizer.success ?? false;
message = energizer.message ?? "Error posting Energizer Orders";
orderData = energizer.data;
}
if (data["fileType"] === "loreal") {
// orders in
}
if (data["fileType"] === "pg") {
// orders in
}
if (data["fileType"] === "macro") {
// orders in
const macro = await macroImportOrders(data["postOrders"], user);
success = macro.success ?? false;
message = macro.message ?? "Error posting Macro Orders";
orderData = macro.data;
}
return {
success,
message,
data: orderData,
};
};

View File

@@ -0,0 +1,62 @@
import axios from "axios";
import { prodEndpointCreation } from "../../../../../globalUtils/createUrl.js";
import { createLog } from "../../../../logger/logger.js";
export const postOrders = async (data: any, user: any) => {
let endpoint = await prodEndpointCreation(
"/public/v1.0/DemandManagement/ORDERS"
);
try {
const results = await axios({
url: endpoint,
method: "POST",
headers: {
"X-API-Key": process.env.TEC_API_KEY || "",
"Content-Type": "application/json",
},
// if a body is sent over it would be like below
data: data,
});
//console.log(results.status);
if (results.data.errors) {
createLog(
"error",
user.username,
"logisitcs",
results.data.errors[0].message
);
return {
success: true,
message: "Error processing orders",
data: results.data.errors[0].message,
};
}
if (results.status === 200) {
createLog(
"info",
user.username,
"logisitcs",
"Orders were processed please check 2.0 for validation and errors"
);
return {
success: true,
message: "Success on posting orders",
data: data,
};
}
} catch (error: any) {
//console.log(`There is an error`, error);
if (error) {
//console.log(error.response.data);
createLog("error", user.username, "logisitcs", error.response.data);
return {
success: false,
message: "There was an error processing data",
data: error.response.data,
};
}
}
};

View File

@@ -0,0 +1,93 @@
import { db } from "../../../../../database/dbclient.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { createLog } from "../../../logger/logger.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { totalInvNoRn } from "../../../sqlServer/querys/dataMart/totalINV.js";
import { invHistoricalData } from "../../../../../database/schema/historicalINV.js";
import { format } from "date-fns-tz";
import { settings } from "../../../../../database/schema/settings.js";
import { sql } from "drizzle-orm";
import { createLogisticsJob } from "../../utils/logisticsIntervals.js";
export const runHistoricalData = async () => {
/**
* Runs a query at shift change on first shift each day this will be the closest date to the true historical data for blocked, consignment
*/
const { data: set, error: setError } = await tryCatch(
db.select().from(settings)
);
if (setError) {
createLog(
"error",
"lst",
"eom",
"There was an error getting eom historical inv data."
);
return;
}
const timeZone = set.filter((n: any) => n.name === "timezone");
createLogisticsJob("histInv", `0 6 * * *`, timeZone[0].value, async () => {
// remove the lotnumber from the query
const updatedQuery = totalInvNoRn.replaceAll(
",IdProdPlanung",
"--,IdProdPlanung"
);
const { data: inv, error: invError } = await tryCatch(
query(updatedQuery, "EOM historical inv")
);
if (invError) {
createLog(
"error",
"lst",
"eom",
"There was an error getting eom historical inv data."
);
return;
}
/**
* add the inv into the hist table
*/
const setting: any = set;
for (let i = 0; i < inv?.data.length; i++) {
const current = inv?.data[i];
const { data, error } = await tryCatch(
db.insert(invHistoricalData).values({
histDate: format(new Date(), "MM-dd-yyyy"),
plantToken: setting.filter(
(n: any) => n.name === "plantToken"
)[0].value,
article: current.av,
articleDescription: current.Alias,
total_QTY: current.Total_PalletQTY,
avaliable_QTY: current.Avaliable_PalletQTY,
coa_QTY: current.COA_QTY,
held_QTY: current.Held_QTY,
consignment: current.Consigment,
//location: integer("location"),
upd_user: "LST",
upd_date: sql`NOW()`,
})
);
createLog("info", "lst", "eom", ` historical data was just added.`);
if (error) {
createLog(
"error",
"lst",
"eom",
`Error addeding historical data, ${error}`
);
}
}
});
};

View File

@@ -0,0 +1,77 @@
import axios from "axios";
import { labelData } from "../../../sqlServer/querys/materialHelpers/labelInfo.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { createLog } from "../../../logger/logger.js";
import { prodEndpointCreation } from "../../../../globalUtils/createUrl.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { db } from "../../../../../database/dbclient.js";
import { commandLog } from "../../../../../database/schema/commandLog.js";
type Data = {
runningNr: string;
lotNum: number;
};
export const consumeMaterial = async (data: Data) => {
const { runningNr, lotNum } = data;
// replace the rn
console.log(data);
const rnReplace = labelData.replaceAll("[rn]", runningNr);
let barcode;
// get the barcode from the running number
try {
const r: any = await query(rnReplace, "labelData");
barcode = r?.data;
} catch (error) {
console.log(error);
createLog("error", "", "logistics", `Error getting barcode: ${error}`);
}
if (barcode.length === 0) {
return {
success: false,
message: "The running number you've entered not on stock.",
};
//throw Error("The provided runningNr is not in stock");
}
// create the url to post
const url = await prodEndpointCreation(
"/public/v1.0/IssueMaterial/ConsumeNonPreparedManualMaterial"
);
const consumeSomething = {
productionLot: lotNum,
barcode: barcode[0]?.barcode,
};
try {
const results = await axios.post(url, consumeSomething, {
headers: {
"X-API-Key": process.env.TEC_API_KEY || "",
"Content-Type": "application/json",
},
});
//console.log(results);
const { data: commandL, error: ce } = await tryCatch(
db.insert(commandLog).values({
commandUsed: "consumeMaterial",
bodySent: data,
})
);
return {
success: true,
message: "Material was consumed",
status: results.status,
};
} catch (error: any) {
return {
success: false,
status: 200,
message: error.response?.data.errors[0].message,
};
}
};

View File

@@ -0,0 +1,109 @@
import axios from "axios";
import { labelData } from "../../../sqlServer/querys/materialHelpers/labelInfo.js";
import { laneInfo } from "../../../sqlServer/querys/materialHelpers/laneInfo.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { createLog } from "../../../logger/logger.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { prodEndpointCreation } from "../../../../globalUtils/createUrl.js";
import { db } from "../../../../../database/dbclient.js";
import { commandLog } from "../../../../../database/schema/commandLog.js";
type Data = {
runningNr: string;
laneName: string;
};
export const returnMaterial = async (data: Data, prod: any) => {
const { runningNr, laneName } = data;
// replace the rn
const rnReplace = labelData.replaceAll("[rn]", runningNr);
// get the lane id by name
const laneQuery = laneInfo.replaceAll("[laneName]", laneName);
let barcode;
// get the barcode from the running number
try {
const r: any = await query(rnReplace, "labelData");
barcode = r?.data;
} catch (error) {
console.log(error);
createLog(
"error",
prod.user.username,
"logistics",
`Error getting barcode: ${error}`
);
}
const { data: l, error: laneError } = await tryCatch(
query(laneQuery, "laneInfo")
);
const laneData: any = l?.data;
if (laneError) {
return {
success: false,
message:
"The lane you entered is either deactivated or dose not exist.",
laneError,
};
}
if (!laneData) {
return {
success: false,
message:
"The lane you entered is either deactivated or dose not exist.",
};
}
if (laneData.length === 0) {
return {
success: false,
message:
"The lane you entered is either deactivated or dose not exist.",
};
}
if (barcode.length === 0) {
return {
success: false,
message: "The running number you've is not in stock.",
};
//throw Error("The provided runningNr is not in stock");
}
// create the url to post
const url = await prodEndpointCreation(
"/public/v1.0/IssueMaterial/ReturnPartiallyConsumedManualMaterial"
);
const returnSomething = {
laneId: laneData[0]?.laneID,
barcode: barcode[0]?.barcode,
};
try {
const results = await axios.post(url, returnSomething, {
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${prod.user.prod}`,
},
});
//console.log(results);
const { data: commandL, error: ce } = await tryCatch(
db.insert(commandLog).values({
commandUsed: "returnMaterial",
bodySent: data,
})
);
return {
success: true,
message: "Material was returned",
status: results.status,
};
} catch (error: any) {
return {
success: false,
status: 200,
message: error.response?.data.errors[0].message,
};
}
};

View File

@@ -0,0 +1,154 @@
import { db } from "../../../../../database/dbclient.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { siloQuery } from "../../../sqlServer/querys/silo/siloQuery.js";
import { postAdjustment } from "./postAdjustment.js";
import { siloAdjustments } from "../../../../../database/schema/siloAdjustments.js";
import { greetingStuff } from "../../../../globalUtils/greetingEmail.js";
import { sendEmail } from "../../../notifications/controller/sendMail.js";
import { settings } from "../../../../../database/schema/settings.js";
import { generateOneTimeKey } from "../../../../globalUtils/singleUseKey.js";
import { eq } from "drizzle-orm";
import {
getSettings,
serverSettings,
} from "../../../server/controller/settings/getSettings.js";
export const createSiloAdjustment = async (
data: any | null,
user: any | null
) => {
/**
* Creates a silo adjustment based off warehouse, location, and qty.
* qty will come from the hmi, prolink, or silo patrol
*/
// const { data: set, error: setError } = await tryCatch(
// db.select().from(settings)
// );
// const { data: set, error: setError } = await tryCatch(getSettings());
// if (setError) {
// return {
// success: false,
// message: `There was an error getting setting data to post to the server.`,
// data: setError,
// };
// }
const set = serverSettings.length === 0 ? [] : serverSettings;
// getting stock data first so we have it prior to the adjustment
const { data: s, error: stockError } = await tryCatch(
query(siloQuery, "Silo data Query")
);
if (stockError) {
return {
success: false,
message: `There was an error getting stock data to post to the server.`,
data: stockError,
};
}
const stock: any = s?.data as any;
const { data: a, error: errorAdj } = await tryCatch(
postAdjustment(data, user.prod)
);
if (errorAdj) {
return {
success: false,
message: `There was an error doing the silo adjustment.`,
data: errorAdj,
};
}
/**
* Checking to see the difference, and send email if +/- 5% will change later if needed
*/
const sa: any = a;
if (!sa.success) {
console.log(`insde error`);
return {
success: sa.success,
message: sa.message,
data: sa.data,
};
}
const stockNummy = stock.filter((s: any) => s.LocationID === data.laneId);
const theDiff =
((data.quantity - stockNummy[0].Stock_Total) /
((data.quantity + stockNummy[0].Stock_Total) / 2)) *
100;
/**
* Post the data to our db.
*/
//console.log(stockNummy);
const { data: postAdj, error: postAdjError } = await tryCatch(
db
.insert(siloAdjustments)
.values({
warehouseID: data.warehouseId,
locationID: data.laneId,
currentStockLevel: stockNummy[0].Stock_Total,
newLevel: data.quantity,
lastDateAdjusted: new Date(stockNummy[0].LastAdjustment),
add_user: user.username,
})
.returning({ id: siloAdjustments.siloAdjust_id })
);
if (postAdjError) {
//console.log(postAdjError);
return {
success: false,
message: `There was an error posting the new adjustment.`,
data: postAdjError,
};
}
let adj: any = a;
if (Math.abs(theDiff) > 5) {
// console.log(`Send for comment due to being: ${theDiff.toFixed(2)}%`);
const server = set.filter((n: any) => n.name === "server");
const port = set.filter((n: any) => n.name === "serverPort");
const key = await generateOneTimeKey();
const updateKey = await db
.update(siloAdjustments)
.set({ commentKey: key })
.where(eq(siloAdjustments.siloAdjust_id, postAdj[0].id));
const emailSetup = {
email: user.email,
subject: `Alert - Siloadjustment was done with a descrepancy of 5% or greater`,
template: "siloAdjustmentComment",
context: {
greeting: await greetingStuff(),
siloName: stockNummy[0].Description,
variance: `${theDiff.toFixed(2)}%`,
currentLevel: stockNummy[0].Stock_Total,
newLevel: data.quantity,
variancePer: 5,
adjustID: `${postAdj[0].id}&${key}`,
server: server[0].value,
port: port[0].value,
},
};
//console.log(emailSetup);
await sendEmail(emailSetup);
return {
success: adj.success,
message: `Silo adjustmnet was completed you will also receive and email due to the adjustment having a variation of ${Math.abs(
theDiff
).toFixed(2)}%`,
data: adj.data,
};
} else {
return { success: adj.success, message: adj.message, data: adj.data };
}
};

Some files were not shown because too many files have changed in this diff Show More