Compare commits
2 Commits
9f26f2334f
...
85577b291f
| Author | SHA1 | Date | |
|---|---|---|---|
| 85577b291f | |||
| f1abe7b33d |
12
database/migrations/0043_free_winter_soldier.sql
Normal file
12
database/migrations/0043_free_winter_soldier.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE "siloAdjustments" (
|
||||
"lsiloAdjust_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"level" integer,
|
||||
"locationID" integer,
|
||||
"currentStockLevel" numeric,
|
||||
"newLevel" numeric,
|
||||
"comments" text DEFAULT '',
|
||||
"dateAdjusted" timestamp DEFAULT now(),
|
||||
"lastDateAdjusted" timestamp DEFAULT now(),
|
||||
"statusMessage" text DEFAULT '',
|
||||
"add_user" text DEFAULT 'LST_Serivce'
|
||||
);
|
||||
5
database/migrations/0044_hot_smasher.sql
Normal file
5
database/migrations/0044_hot_smasher.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE "siloAdjustments" ADD COLUMN "comment" text DEFAULT '';--> statement-breakpoint
|
||||
ALTER TABLE "siloAdjustments" ADD COLUMN "commentAddedBy" text;--> statement-breakpoint
|
||||
ALTER TABLE "siloAdjustments" ADD COLUMN "commentDate" text;--> statement-breakpoint
|
||||
ALTER TABLE "siloAdjustments" DROP COLUMN "comments";--> statement-breakpoint
|
||||
ALTER TABLE "siloAdjustments" DROP COLUMN "statusMessage";
|
||||
1
database/migrations/0045_heavy_ravenous.sql
Normal file
1
database/migrations/0045_heavy_ravenous.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "siloAdjustments" ADD COLUMN "commentKey" text;
|
||||
1
database/migrations/0046_keen_firebird.sql
Normal file
1
database/migrations/0046_keen_firebird.sql
Normal file
@@ -0,0 +1 @@
|
||||
CREATE UNIQUE INDEX "subModule_name" ON "subModules" USING btree ("name");
|
||||
1
database/migrations/0047_silky_starbolt.sql
Normal file
1
database/migrations/0047_silky_starbolt.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "subModules" ADD COLUMN "icon" text;
|
||||
1539
database/migrations/meta/0043_snapshot.json
Normal file
1539
database/migrations/meta/0043_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1544
database/migrations/meta/0044_snapshot.json
Normal file
1544
database/migrations/meta/0044_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1550
database/migrations/meta/0045_snapshot.json
Normal file
1550
database/migrations/meta/0045_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1566
database/migrations/meta/0046_snapshot.json
Normal file
1566
database/migrations/meta/0046_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1572
database/migrations/meta/0047_snapshot.json
Normal file
1572
database/migrations/meta/0047_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -302,6 +302,41 @@
|
||||
"when": 1743778477759,
|
||||
"tag": "0042_big_power_pack",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 43,
|
||||
"version": "7",
|
||||
"when": 1743809547351,
|
||||
"tag": "0043_free_winter_soldier",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 44,
|
||||
"version": "7",
|
||||
"when": 1743811709366,
|
||||
"tag": "0044_hot_smasher",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 45,
|
||||
"version": "7",
|
||||
"when": 1743819367359,
|
||||
"tag": "0045_heavy_ravenous",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 46,
|
||||
"version": "7",
|
||||
"when": 1743821039322,
|
||||
"tag": "0046_keen_firebird",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 47,
|
||||
"version": "7",
|
||||
"when": 1743822056329,
|
||||
"tag": "0047_silky_starbolt",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
39
database/schema/siloAdjustments.ts
Normal file
39
database/schema/siloAdjustments.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
text,
|
||||
pgTable,
|
||||
numeric,
|
||||
timestamp,
|
||||
uuid,
|
||||
integer,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
export const siloAdjustments = pgTable(
|
||||
"siloAdjustments",
|
||||
{
|
||||
siloAdjust_id: uuid("lsiloAdjust_id").defaultRandom().primaryKey(),
|
||||
warehouseID: integer("level"),
|
||||
locationID: integer("locationID"),
|
||||
currentStockLevel: numeric("currentStockLevel"),
|
||||
newLevel: numeric("newLevel"),
|
||||
comment: text("comment").default(""),
|
||||
dateAdjusted: timestamp("dateAdjusted").defaultNow(),
|
||||
lastDateAdjusted: timestamp("lastDateAdjusted").defaultNow(),
|
||||
commentAddedBy: text("commentAddedBy"),
|
||||
commentDate: text("commentDate"),
|
||||
commentKey: text("commentKey"),
|
||||
add_user: text("add_user").default("LST_Serivce"),
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
// uniqueIndex("role_name").on(table.name),
|
||||
]
|
||||
);
|
||||
|
||||
// Schema for inserting a user - can be used to validate API requests
|
||||
// export const insertRolesSchema = createInsertSchema(roles, {
|
||||
// name: z.string().min(3, {message: "Role name must be more than 3 letters"}),
|
||||
// });
|
||||
// Schema for selecting a Expenses - can be used to validate API responses
|
||||
export const selectRolesSchema = createSelectSchema(siloAdjustments);
|
||||
@@ -23,16 +23,17 @@ export const subModules = pgTable(
|
||||
link: text("link").notNull(),
|
||||
active: boolean("active").default(false),
|
||||
roles: jsonb("roles").notNull().default(["systemAdmin"]), // ["view", "technician", "supervisor","manager", "admin","systemAdmin"]
|
||||
icon: text("icon"),
|
||||
subSubModule: jsonb("subSubModule").default([]),
|
||||
add_User: text("add_User").default("LST_System").notNull(),
|
||||
add_Date: timestamp("add_Date").defaultNow(),
|
||||
upd_user: text("upd_User").default("LST_System").notNull(),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
}
|
||||
// (table) => [
|
||||
// // uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
// uniqueIndex("subModule_name").on(table.name),
|
||||
// ]
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
uniqueIndex("subModule_name").on(table.name),
|
||||
]
|
||||
);
|
||||
|
||||
// Schema for inserting a user - can be used to validate API requests
|
||||
|
||||
@@ -3,7 +3,7 @@ import { LstCard } from "../extendedUI/LstCard";
|
||||
import { CardHeader } from "../ui/card";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
import { useRouter } from "@tanstack/react-router";
|
||||
import { useRouter, useSearch } from "@tanstack/react-router";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Label } from "../ui/label";
|
||||
@@ -22,6 +22,7 @@ const LoginForm = () => {
|
||||
const rememeberMe = localStorage.getItem("rememberMe") === "true";
|
||||
const username = localStorage.getItem("username") || "";
|
||||
const router = useRouter();
|
||||
const search = useSearch({ from: "/login" });
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
@@ -65,12 +66,16 @@ const LoginForm = () => {
|
||||
// Store token in localStorage
|
||||
// localStorage.setItem("auth_token", data.data.token);
|
||||
if (data.success) {
|
||||
const prod = btoa(`${value.username.toLowerCase()}:${value.password}`);
|
||||
const prod = btoa(
|
||||
`${value.username.toLowerCase()}:${value.password}`
|
||||
);
|
||||
const prodUser = { ...data.user, prod: prod };
|
||||
|
||||
setSession(prodUser, data.token);
|
||||
toast.success(`You are logged in as ${data.user.username}`);
|
||||
router.navigate({ to: "/" });
|
||||
|
||||
console.log(search.redirect ? search.redirect : "oops");
|
||||
router.history.push(search.redirect ? search.redirect : "/");
|
||||
}
|
||||
|
||||
if (!data.success) {
|
||||
@@ -117,7 +122,9 @@ const LoginForm = () => {
|
||||
<Input
|
||||
type="password"
|
||||
{...register("password")}
|
||||
className={errors.password ? "border-red-500" : ""}
|
||||
className={
|
||||
errors.password ? "border-red-500" : ""
|
||||
}
|
||||
aria-invalid={!!errors.password}
|
||||
/>
|
||||
{errors.password && (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Cylinder, Package, Truck } from "lucide-react";
|
||||
//import { Cylinder, Package, Truck } from "lucide-react";
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
@@ -9,67 +9,68 @@ import {
|
||||
} from "../../ui/sidebar";
|
||||
import { hasPageAccess } from "@/utils/userAccess";
|
||||
import { User } from "@/types/users";
|
||||
import { useSubModuleStore } from "@/lib/store/useSubModuleStore";
|
||||
// this will need to be moved to a links section the db to make it more easy to remove and add
|
||||
const items = [
|
||||
{
|
||||
title: "Silo Adjustments",
|
||||
url: "#",
|
||||
icon: Cylinder,
|
||||
role: ["admin", "systemAdmin"],
|
||||
module: "logistics",
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
name: "Bulk orders",
|
||||
moduleName: "logistics",
|
||||
description: "",
|
||||
link: "#",
|
||||
icon: Truck,
|
||||
role: ["systemAdmin"],
|
||||
active: true,
|
||||
subSubModule: [],
|
||||
},
|
||||
{
|
||||
name: "Forecast",
|
||||
moduleName: "logistics",
|
||||
description: "",
|
||||
link: "#",
|
||||
icon: Truck,
|
||||
role: ["systemAdmin"],
|
||||
active: true,
|
||||
subSubModule: [],
|
||||
},
|
||||
{
|
||||
name: "Ocme cycle counts",
|
||||
moduleName: "logistics",
|
||||
description: "",
|
||||
link: "#",
|
||||
icon: Package,
|
||||
role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
|
||||
active: false,
|
||||
subSubModule: [],
|
||||
},
|
||||
{
|
||||
name: "Material Helper",
|
||||
moduleName: "logistics",
|
||||
description: "",
|
||||
link: "/materialHelper/consumption",
|
||||
icon: Package,
|
||||
role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
|
||||
active: true,
|
||||
subSubModule: [],
|
||||
},
|
||||
{
|
||||
name: "Ocme Cyclecount",
|
||||
moduleName: "logistics",
|
||||
description: "",
|
||||
link: "/cyclecount",
|
||||
icon: Package,
|
||||
role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
|
||||
active: true,
|
||||
subSubModule: [],
|
||||
},
|
||||
];
|
||||
// const items = [
|
||||
// {
|
||||
// title: "Silo Adjustments",
|
||||
// url: "#",
|
||||
// icon: Cylinder,
|
||||
// role: ["admin", "systemAdmin"],
|
||||
// module: "logistics",
|
||||
// active: true,
|
||||
// },
|
||||
// {
|
||||
// name: "Bulk orders",
|
||||
// moduleName: "logistics",
|
||||
// description: "",
|
||||
// link: "#",
|
||||
// icon: Truck,
|
||||
// role: ["systemAdmin"],
|
||||
// active: true,
|
||||
// subSubModule: [],
|
||||
// },
|
||||
// {
|
||||
// name: "Forecast",
|
||||
// moduleName: "logistics",
|
||||
// description: "",
|
||||
// link: "#",
|
||||
// icon: Truck,
|
||||
// role: ["systemAdmin"],
|
||||
// active: true,
|
||||
// subSubModule: [],
|
||||
// },
|
||||
// {
|
||||
// name: "Ocme cycle counts",
|
||||
// moduleName: "logistics",
|
||||
// description: "",
|
||||
// link: "#",
|
||||
// icon: Package,
|
||||
// role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
|
||||
// active: false,
|
||||
// subSubModule: [],
|
||||
// },
|
||||
// {
|
||||
// name: "Material Helper",
|
||||
// moduleName: "logistics",
|
||||
// description: "",
|
||||
// link: "/materialHelper/consumption",
|
||||
// icon: Package,
|
||||
// role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
|
||||
// active: true,
|
||||
// subSubModule: [],
|
||||
// },
|
||||
// {
|
||||
// name: "Ocme Cyclecount",
|
||||
// moduleName: "logistics",
|
||||
// description: "",
|
||||
// link: "/cyclecount",
|
||||
// icon: Package,
|
||||
// role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
|
||||
// active: true,
|
||||
// subSubModule: [],
|
||||
// },
|
||||
// ];
|
||||
|
||||
export function LogisticsSideBar({
|
||||
user,
|
||||
@@ -78,26 +79,35 @@ export function LogisticsSideBar({
|
||||
user: User | null;
|
||||
moduleID: string;
|
||||
}) {
|
||||
const { subModules } = useSubModuleStore();
|
||||
|
||||
const items = subModules.filter((m) => m.moduleName === "logistics");
|
||||
console.log(items);
|
||||
return (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Logistics</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
{items.map((item) => {
|
||||
return (
|
||||
<SidebarMenuItem key={item.submodule_id}>
|
||||
<>
|
||||
{hasPageAccess(user, item.role, moduleID) &&
|
||||
{hasPageAccess(
|
||||
user,
|
||||
item.roles,
|
||||
moduleID
|
||||
) &&
|
||||
item.active && (
|
||||
<SidebarMenuButton asChild>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
<a href={item.link}>
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
)}
|
||||
</>
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
|
||||
120
frontend/src/components/logistics/siloAdjustments/Comment.tsx
Normal file
120
frontend/src/components/logistics/siloAdjustments/Comment.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { LstCard } from "@/components/extendedUI/LstCard";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CardContent, CardFooter, CardHeader } from "@/components/ui/card";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
|
||||
import { useForm } from "@tanstack/react-form";
|
||||
import { useRouter } from "@tanstack/react-router";
|
||||
import axios from "axios";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export default function Comment(data: any) {
|
||||
const token = localStorage.getItem("auth_token");
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
comment: "",
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`/api/logistics/postcomment/${data.id.split("&")[0]}`,
|
||||
{
|
||||
comment: value.comment,
|
||||
key: data.id.split("&")[1],
|
||||
},
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
);
|
||||
|
||||
if (res.data.success) {
|
||||
toast.success(res.data.message);
|
||||
form.reset();
|
||||
router.navigate({ to: "/siloAdjustments" });
|
||||
}
|
||||
|
||||
if (!res.data.success) {
|
||||
toast.error(res.data.message);
|
||||
form.reset();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error(`There was an error posting your comment.`);
|
||||
}
|
||||
setIsSubmitting(false);
|
||||
},
|
||||
});
|
||||
return (
|
||||
<div className="">
|
||||
<LstCard>
|
||||
<CardHeader>
|
||||
Please enter your comment for the silo adjust
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<form.Field
|
||||
name="comment"
|
||||
validators={{
|
||||
// We can choose between form-wide and field-specific validators
|
||||
onChange: ({ value }) =>
|
||||
value.length > 10
|
||||
? undefined
|
||||
: "Comment must be longer than 10 characters.",
|
||||
}}
|
||||
children={(field) => {
|
||||
return (
|
||||
<div className="m-2 min-w-48 max-w-96 p-2">
|
||||
<Label
|
||||
htmlFor="comment"
|
||||
className="mb-2"
|
||||
>
|
||||
Comment
|
||||
</Label>
|
||||
<Textarea
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
//type="number"
|
||||
onChange={(e) =>
|
||||
field.handleChange(
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
/>
|
||||
{field.state.meta.errors.length ? (
|
||||
<em>
|
||||
{field.state.meta.errors.join(
|
||||
","
|
||||
)}
|
||||
</em>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
onClick={form.handleSubmit}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</LstCard>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { useModuleStore } from "../../lib/store/useModuleStore";
|
||||
import { useEffect } from "react";
|
||||
import { useSettingStore } from "@/lib/store/useSettings";
|
||||
import { useGetUserRoles } from "@/lib/store/useGetRoles";
|
||||
import { useSubModuleStore } from "@/lib/store/useSubModuleStore";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
@@ -14,11 +15,13 @@ export const SessionProvider = ({
|
||||
const { fetchModules } = useModuleStore();
|
||||
const { fetchSettings } = useSettingStore();
|
||||
const { fetchUserRoles } = useGetUserRoles();
|
||||
const { fetchSubModules } = useSubModuleStore();
|
||||
|
||||
useEffect(() => {
|
||||
fetchModules();
|
||||
fetchSettings();
|
||||
fetchUserRoles();
|
||||
fetchSubModules();
|
||||
}, []);
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
|
||||
30
frontend/src/lib/store/useSubModuleStore.ts
Normal file
30
frontend/src/lib/store/useSubModuleStore.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { SubModules } from "@/types/modules";
|
||||
import axios from "axios";
|
||||
import { create } from "zustand";
|
||||
|
||||
interface SettingState {
|
||||
subModules: SubModules[];
|
||||
|
||||
fetchSubModules: () => Promise<void>;
|
||||
setSubModules: (modules: SubModules[]) => void;
|
||||
}
|
||||
interface FetchModulesResponse {
|
||||
data: SubModules[];
|
||||
}
|
||||
|
||||
export const useSubModuleStore = create<SettingState>()((set) => ({
|
||||
subModules: [],
|
||||
setSubModules: (subModules) => set({ subModules }),
|
||||
fetchSubModules: async () => {
|
||||
try {
|
||||
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
|
||||
const response = await axios.get(`/api/server/submodules`, {});
|
||||
const data: FetchModulesResponse = response.data; //await response.json();
|
||||
//console.log(data);
|
||||
set({ subModules: data.data });
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch settings:", error);
|
||||
set({ subModules: [] });
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -25,10 +25,12 @@ import { Route as AdminSettingsImport } from './routes/_admin/settings'
|
||||
import { Route as AdminServersImport } from './routes/_admin/servers'
|
||||
import { Route as AdminModulesImport } from './routes/_admin/modules'
|
||||
import { Route as ocmeCyclecountIndexImport } from './routes/(ocme)/cyclecount/index'
|
||||
import { Route as logisticsSiloAdjustmentsIndexImport } from './routes/(logistics)/siloAdjustments/index'
|
||||
import { Route as logisticsMaterialHelperIndexImport } from './routes/(logistics)/materialHelper/index'
|
||||
import { Route as EomArticleAvImport } from './routes/_eom/article/$av'
|
||||
import { Route as logisticsMaterialHelperSiloLinkIndexImport } from './routes/(logistics)/materialHelper/siloLink/index'
|
||||
import { Route as logisticsMaterialHelperConsumptionIndexImport } from './routes/(logistics)/materialHelper/consumption/index'
|
||||
import { Route as logisticsSiloAdjustmentsCommentCommentImport } from './routes/(logistics)/siloAdjustments/comment/$comment'
|
||||
|
||||
// Create/Update Routes
|
||||
|
||||
@@ -113,6 +115,13 @@ const ocmeCyclecountIndexRoute = ocmeCyclecountIndexImport.update({
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const logisticsSiloAdjustmentsIndexRoute =
|
||||
logisticsSiloAdjustmentsIndexImport.update({
|
||||
id: '/(logistics)/siloAdjustments/',
|
||||
path: '/siloAdjustments/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const logisticsMaterialHelperIndexRoute =
|
||||
logisticsMaterialHelperIndexImport.update({
|
||||
id: '/(logistics)/materialHelper/',
|
||||
@@ -140,6 +149,13 @@ const logisticsMaterialHelperConsumptionIndexRoute =
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const logisticsSiloAdjustmentsCommentCommentRoute =
|
||||
logisticsSiloAdjustmentsCommentCommentImport.update({
|
||||
id: '/(logistics)/siloAdjustments/comment/$comment',
|
||||
path: '/siloAdjustments/comment/$comment',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
// Populate the FileRoutesByPath interface
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
@@ -249,6 +265,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof logisticsMaterialHelperIndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/(logistics)/siloAdjustments/': {
|
||||
id: '/(logistics)/siloAdjustments/'
|
||||
path: '/siloAdjustments'
|
||||
fullPath: '/siloAdjustments'
|
||||
preLoaderRoute: typeof logisticsSiloAdjustmentsIndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/(ocme)/cyclecount/': {
|
||||
id: '/(ocme)/cyclecount/'
|
||||
path: '/cyclecount'
|
||||
@@ -256,6 +279,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof ocmeCyclecountIndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/(logistics)/siloAdjustments/comment/$comment': {
|
||||
id: '/(logistics)/siloAdjustments/comment/$comment'
|
||||
path: '/siloAdjustments/comment/$comment'
|
||||
fullPath: '/siloAdjustments/comment/$comment'
|
||||
preLoaderRoute: typeof logisticsSiloAdjustmentsCommentCommentImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/(logistics)/materialHelper/consumption/': {
|
||||
id: '/(logistics)/materialHelper/consumption/'
|
||||
path: '/materialHelper/consumption'
|
||||
@@ -327,7 +357,9 @@ export interface FileRoutesByFullPath {
|
||||
'/ocp': typeof OcpIndexRoute
|
||||
'/article/$av': typeof EomArticleAvRoute
|
||||
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
|
||||
'/siloAdjustments': typeof logisticsSiloAdjustmentsIndexRoute
|
||||
'/cyclecount': typeof ocmeCyclecountIndexRoute
|
||||
'/siloAdjustments/comment/$comment': typeof logisticsSiloAdjustmentsCommentCommentRoute
|
||||
'/materialHelper/consumption': typeof logisticsMaterialHelperConsumptionIndexRoute
|
||||
'/materialHelper/siloLink': typeof logisticsMaterialHelperSiloLinkIndexRoute
|
||||
}
|
||||
@@ -346,7 +378,9 @@ export interface FileRoutesByTo {
|
||||
'/ocp': typeof OcpIndexRoute
|
||||
'/article/$av': typeof EomArticleAvRoute
|
||||
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
|
||||
'/siloAdjustments': typeof logisticsSiloAdjustmentsIndexRoute
|
||||
'/cyclecount': typeof ocmeCyclecountIndexRoute
|
||||
'/siloAdjustments/comment/$comment': typeof logisticsSiloAdjustmentsCommentCommentRoute
|
||||
'/materialHelper/consumption': typeof logisticsMaterialHelperConsumptionIndexRoute
|
||||
'/materialHelper/siloLink': typeof logisticsMaterialHelperSiloLinkIndexRoute
|
||||
}
|
||||
@@ -368,7 +402,9 @@ export interface FileRoutesById {
|
||||
'/ocp/': typeof OcpIndexRoute
|
||||
'/_eom/article/$av': typeof EomArticleAvRoute
|
||||
'/(logistics)/materialHelper/': typeof logisticsMaterialHelperIndexRoute
|
||||
'/(logistics)/siloAdjustments/': typeof logisticsSiloAdjustmentsIndexRoute
|
||||
'/(ocme)/cyclecount/': typeof ocmeCyclecountIndexRoute
|
||||
'/(logistics)/siloAdjustments/comment/$comment': typeof logisticsSiloAdjustmentsCommentCommentRoute
|
||||
'/(logistics)/materialHelper/consumption/': typeof logisticsMaterialHelperConsumptionIndexRoute
|
||||
'/(logistics)/materialHelper/siloLink/': typeof logisticsMaterialHelperSiloLinkIndexRoute
|
||||
}
|
||||
@@ -389,7 +425,9 @@ export interface FileRouteTypes {
|
||||
| '/ocp'
|
||||
| '/article/$av'
|
||||
| '/materialHelper'
|
||||
| '/siloAdjustments'
|
||||
| '/cyclecount'
|
||||
| '/siloAdjustments/comment/$comment'
|
||||
| '/materialHelper/consumption'
|
||||
| '/materialHelper/siloLink'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
@@ -407,7 +445,9 @@ export interface FileRouteTypes {
|
||||
| '/ocp'
|
||||
| '/article/$av'
|
||||
| '/materialHelper'
|
||||
| '/siloAdjustments'
|
||||
| '/cyclecount'
|
||||
| '/siloAdjustments/comment/$comment'
|
||||
| '/materialHelper/consumption'
|
||||
| '/materialHelper/siloLink'
|
||||
id:
|
||||
@@ -427,7 +467,9 @@ export interface FileRouteTypes {
|
||||
| '/ocp/'
|
||||
| '/_eom/article/$av'
|
||||
| '/(logistics)/materialHelper/'
|
||||
| '/(logistics)/siloAdjustments/'
|
||||
| '/(ocme)/cyclecount/'
|
||||
| '/(logistics)/siloAdjustments/comment/$comment'
|
||||
| '/(logistics)/materialHelper/consumption/'
|
||||
| '/(logistics)/materialHelper/siloLink/'
|
||||
fileRoutesById: FileRoutesById
|
||||
@@ -442,7 +484,9 @@ export interface RootRouteChildren {
|
||||
LoginRoute: typeof LoginRoute
|
||||
OcpIndexRoute: typeof OcpIndexRoute
|
||||
logisticsMaterialHelperIndexRoute: typeof logisticsMaterialHelperIndexRoute
|
||||
logisticsSiloAdjustmentsIndexRoute: typeof logisticsSiloAdjustmentsIndexRoute
|
||||
ocmeCyclecountIndexRoute: typeof ocmeCyclecountIndexRoute
|
||||
logisticsSiloAdjustmentsCommentCommentRoute: typeof logisticsSiloAdjustmentsCommentCommentRoute
|
||||
logisticsMaterialHelperConsumptionIndexRoute: typeof logisticsMaterialHelperConsumptionIndexRoute
|
||||
logisticsMaterialHelperSiloLinkIndexRoute: typeof logisticsMaterialHelperSiloLinkIndexRoute
|
||||
}
|
||||
@@ -456,7 +500,10 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
LoginRoute: LoginRoute,
|
||||
OcpIndexRoute: OcpIndexRoute,
|
||||
logisticsMaterialHelperIndexRoute: logisticsMaterialHelperIndexRoute,
|
||||
logisticsSiloAdjustmentsIndexRoute: logisticsSiloAdjustmentsIndexRoute,
|
||||
ocmeCyclecountIndexRoute: ocmeCyclecountIndexRoute,
|
||||
logisticsSiloAdjustmentsCommentCommentRoute:
|
||||
logisticsSiloAdjustmentsCommentCommentRoute,
|
||||
logisticsMaterialHelperConsumptionIndexRoute:
|
||||
logisticsMaterialHelperConsumptionIndexRoute,
|
||||
logisticsMaterialHelperSiloLinkIndexRoute:
|
||||
@@ -481,7 +528,9 @@ export const routeTree = rootRoute
|
||||
"/login",
|
||||
"/ocp/",
|
||||
"/(logistics)/materialHelper/",
|
||||
"/(logistics)/siloAdjustments/",
|
||||
"/(ocme)/cyclecount/",
|
||||
"/(logistics)/siloAdjustments/comment/$comment",
|
||||
"/(logistics)/materialHelper/consumption/",
|
||||
"/(logistics)/materialHelper/siloLink/"
|
||||
]
|
||||
@@ -551,9 +600,15 @@ export const routeTree = rootRoute
|
||||
"/(logistics)/materialHelper/": {
|
||||
"filePath": "(logistics)/materialHelper/index.tsx"
|
||||
},
|
||||
"/(logistics)/siloAdjustments/": {
|
||||
"filePath": "(logistics)/siloAdjustments/index.tsx"
|
||||
},
|
||||
"/(ocme)/cyclecount/": {
|
||||
"filePath": "(ocme)/cyclecount/index.tsx"
|
||||
},
|
||||
"/(logistics)/siloAdjustments/comment/$comment": {
|
||||
"filePath": "(logistics)/siloAdjustments/comment/$comment.tsx"
|
||||
},
|
||||
"/(logistics)/materialHelper/consumption/": {
|
||||
"filePath": "(logistics)/materialHelper/consumption/index.tsx"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import Comment from "@/components/logistics/siloAdjustments/Comment";
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/(logistics)/siloAdjustments/comment/$comment"
|
||||
)({
|
||||
beforeLoad: async () => {
|
||||
const auth = localStorage.getItem("auth_token");
|
||||
if (!auth) {
|
||||
throw redirect({
|
||||
to: "/login",
|
||||
search: {
|
||||
// Use the current location to power a redirect after login
|
||||
// (Do not use `router.state.resolvedLocation` as it can
|
||||
// potentially lag behind the actual current location)
|
||||
redirect: location.pathname + location.search,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
// In a loader
|
||||
loader: ({ params }) => params.comment,
|
||||
// Or in a component
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { comment } = Route.useParams();
|
||||
return (
|
||||
<div className="ml-20 mt-20">
|
||||
<Comment id={comment} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/(logistics)/siloAdjustments/')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/(logistics)/siloAdjustments/"!</div>
|
||||
}
|
||||
@@ -7,6 +7,12 @@ export const Route = createFileRoute("/_admin")({
|
||||
if (!auth) {
|
||||
throw redirect({
|
||||
to: "/login",
|
||||
search: {
|
||||
// Use the current location to power a redirect after login
|
||||
// (Do not use `router.state.resolvedLocation` as it can
|
||||
// potentially lag behind the actual current location)
|
||||
redirect: location.pathname + location.search,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,6 +7,12 @@ export const Route = createFileRoute("/_auth")({
|
||||
if (!auth) {
|
||||
throw redirect({
|
||||
to: "/login",
|
||||
search: {
|
||||
// Use the current location to power a redirect after login
|
||||
// (Do not use `router.state.resolvedLocation` as it can
|
||||
// potentially lag behind the actual current location)
|
||||
redirect: location.href,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
|
||||
import LoginForm from "@/components/auth/LoginForm";
|
||||
import { z } from "zod";
|
||||
|
||||
export const Route = createFileRoute("/login")({
|
||||
component: RouteComponent,
|
||||
@@ -12,6 +13,9 @@ export const Route = createFileRoute("/login")({
|
||||
});
|
||||
}
|
||||
},
|
||||
validateSearch: z.object({
|
||||
redirect: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
|
||||
@@ -8,3 +8,13 @@ export interface Modules {
|
||||
upd_user: string;
|
||||
upd_date: Date;
|
||||
}
|
||||
|
||||
export interface SubModules {
|
||||
submodule_id: string;
|
||||
name: string;
|
||||
link: string;
|
||||
icon: string;
|
||||
moduleName: string;
|
||||
active: boolean;
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
32
frontend/src/utils/formStuff/options/InputField.tsx
Normal file
32
frontend/src/utils/formStuff/options/InputField.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
//import { Input } from "@/components/ui/input";
|
||||
//import { Label } from "@radix-ui/react-dropdown-menu";
|
||||
|
||||
// export const FormInput = (form: any, label: string) => {
|
||||
// // <form.Field
|
||||
// // name="username"
|
||||
// // validators={{
|
||||
// // // We can choose between form-wide and field-specific validators
|
||||
// // onChange: ({ value }) =>
|
||||
// // value.length > 3
|
||||
// // ? undefined
|
||||
// // : "Username must be longer than 3 letters",
|
||||
// // }}
|
||||
// // children={(field) => {
|
||||
// // return (
|
||||
// // <div className="m-2 min-w-48 max-w-96 p-2">
|
||||
// // <Label htmlFor="username">{label}</Label>
|
||||
// // <Input
|
||||
// // name={field.name}
|
||||
// // value={field.state.value}
|
||||
// // onBlur={field.handleBlur}
|
||||
// // //type="number"
|
||||
// // onChange={(e) => field.handleChange(e.target.value)}
|
||||
// // />
|
||||
// // {field.state.meta.errors.length ? (
|
||||
// // <em>{field.state.meta.errors.join(",")}</em>
|
||||
// // ) : null}
|
||||
// // </div>
|
||||
// // );
|
||||
// // }}
|
||||
// // />;
|
||||
// };
|
||||
12
server/globalUtils/greetingEmail.ts
Normal file
12
server/globalUtils/greetingEmail.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { getHours } from "date-fns";
|
||||
export const greetingStuff = async (date = new Date()) => {
|
||||
const hour = getHours(date);
|
||||
|
||||
if (hour < 12) {
|
||||
return "Good morning";
|
||||
} else if (hour < 18) {
|
||||
return "Good afternoon";
|
||||
} else {
|
||||
return "Good evening";
|
||||
}
|
||||
};
|
||||
11
server/globalUtils/singleUseKey.ts
Normal file
11
server/globalUtils/singleUseKey.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import crypto from "crypto";
|
||||
|
||||
export const generateOneTimeKey = async (length = 32) => {
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
let key = "";
|
||||
const bytes = crypto.randomBytes(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
key += chars[bytes[i] % chars.length];
|
||||
}
|
||||
return key.match(/.{1,4}/g)!.join("-"); // group by 4 chars
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
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";
|
||||
|
||||
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)
|
||||
);
|
||||
|
||||
if (setError) {
|
||||
return {
|
||||
success: false,
|
||||
message: `There was an error getting setting data to post to the server.`,
|
||||
data: setError,
|
||||
};
|
||||
}
|
||||
|
||||
// getting stock data first so we have it prior to the adjustment
|
||||
const { data: stock, 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 { 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 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,
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
let adj: any = a;
|
||||
return { success: adj.success, message: adj.message, data: adj.data };
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
import axios from "axios";
|
||||
import { prodEndpointCreation } from "../../../../globalUtils/createUrl.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
|
||||
export const postAdjustment = async (data: any, prod: any) => {
|
||||
if (data.warehouseId === undefined) {
|
||||
return {
|
||||
sucess: false,
|
||||
message: `Missing mandatory field: warehouseID`,
|
||||
data: { error: `Missing mandatory field: warehouseID` },
|
||||
};
|
||||
}
|
||||
|
||||
if (data.laneId === undefined) {
|
||||
return {
|
||||
sucess: false,
|
||||
message: `Missing mandatory field: locationID`,
|
||||
data: { error: `Missing mandatory field: locationID` },
|
||||
};
|
||||
}
|
||||
|
||||
if (data.quantity == "0") {
|
||||
return {
|
||||
sucess: false,
|
||||
message: `You entered 0 for the quantity to post, quantity needs to be at leave 1`,
|
||||
data: {
|
||||
error: `You entered 0 for the quantity to post, quantity needs to be at leave 1`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const siloAdjustment = {
|
||||
warehouseId: data.warehouseId,
|
||||
laneId: data.laneId,
|
||||
quantity: data.quantity,
|
||||
};
|
||||
|
||||
let url = await prodEndpointCreation(
|
||||
"/public/v1.0/Warehousing/AdjustSiloStockLevel"
|
||||
);
|
||||
|
||||
const { data: silo, error } = await tryCatch(
|
||||
axios.post(url, siloAdjustment, {
|
||||
headers: { Authorization: `Basic ${prod}` },
|
||||
})
|
||||
);
|
||||
let e = error as any;
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error in posting the silo adjustment.",
|
||||
data: {
|
||||
status: e.response?.status,
|
||||
statusText: e.response?.statusText,
|
||||
data: e.response?.data,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (silo.status !== 200) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error in posting the silo adjustment",
|
||||
data: {
|
||||
status: silo.status,
|
||||
statusText: silo.statusText,
|
||||
data: silo.data,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Adjustment was completed",
|
||||
data: {
|
||||
status: silo.status,
|
||||
statusText: silo.statusText,
|
||||
data: silo.data,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { siloAdjustments } from "../../../../../database/schema/siloAdjustments.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
|
||||
export const postSiloComment = async (
|
||||
id: string,
|
||||
comment: string,
|
||||
commentk: string,
|
||||
user: any
|
||||
) => {
|
||||
/**
|
||||
* We will add the comment to the silo adjustment so we know the why we had this.
|
||||
*/
|
||||
|
||||
// make sure we havea valid key
|
||||
const { data: key, error: keyErro } = await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(siloAdjustments)
|
||||
.where(eq(siloAdjustments.siloAdjust_id, id))
|
||||
);
|
||||
|
||||
if (keyErro) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the adjustment.",
|
||||
data: keyErro,
|
||||
};
|
||||
}
|
||||
|
||||
if (key[0].commentKey != commentk) {
|
||||
return {
|
||||
success: false,
|
||||
message: "The key you provided is invalid.",
|
||||
data: keyErro,
|
||||
};
|
||||
}
|
||||
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.update(siloAdjustments)
|
||||
.set({
|
||||
comment: comment,
|
||||
commentAddedBy: user.username,
|
||||
commentDate: sql`NOW()`,
|
||||
commentKey: null,
|
||||
})
|
||||
.where(eq(siloAdjustments.siloAdjust_id, id))
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error adding the comment.",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Comment was successfully added.",
|
||||
};
|
||||
};
|
||||
@@ -2,9 +2,18 @@ import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
|
||||
import comsumeMaterial from "./route/consumeMaterial.js";
|
||||
import returnMat from "./route/returnMaterial.js";
|
||||
import createSiloAdjustment from "./route/siloAdjustments/createSiloAdjustment.js";
|
||||
import postComment from "./route/siloAdjustments/postComment.js";
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const routes = [comsumeMaterial, returnMat] as const;
|
||||
const routes = [
|
||||
comsumeMaterial,
|
||||
returnMat,
|
||||
|
||||
// silo
|
||||
createSiloAdjustment,
|
||||
postComment,
|
||||
] as const;
|
||||
|
||||
// app.route("/server", modules);
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { verify } from "hono/jwt";
|
||||
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js";
|
||||
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
|
||||
import { createSiloAdjustment } from "../../controller/siloAdjustments/createSiloAdjustment.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const responseSchema = z.object({
|
||||
success: z.boolean().optional().openapi({ example: true }),
|
||||
message: z.string().optional().openapi({ example: "user access" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["logistics"],
|
||||
summary: "Creates silo adjustmennt",
|
||||
method: "post",
|
||||
path: "/createsiloadjustment",
|
||||
middleware: authMiddleware,
|
||||
description:
|
||||
"Creates a silo adjustment for the silo if and stores the stock numbers.",
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//apiHit(c, { endpoint: "api/sqlProd/close" });
|
||||
const authHeader = c.req.header("Authorization");
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
|
||||
try {
|
||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
try {
|
||||
//return apiReturn(c, true, access?.message, access?.data, 200);
|
||||
const data = await c.req.json();
|
||||
const createSiloAdj = await createSiloAdjustment(
|
||||
data,
|
||||
payload.user
|
||||
);
|
||||
return c.json(
|
||||
{
|
||||
success: createSiloAdj.success,
|
||||
message: createSiloAdj.message,
|
||||
data: createSiloAdj.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: "Missing data please try again",
|
||||
error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
return c.json({ success: false, message: "Unauthorized" }, 401);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
@@ -0,0 +1,85 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { verify } from "hono/jwt";
|
||||
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js";
|
||||
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
|
||||
import { createSiloAdjustment } from "../../controller/siloAdjustments/createSiloAdjustment.js";
|
||||
import { postSiloComment } from "../../controller/siloAdjustments/postComment.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const ParamsSchema = z.object({
|
||||
adjId: z
|
||||
.string()
|
||||
.min(3)
|
||||
.openapi({
|
||||
param: {
|
||||
name: "adjId",
|
||||
in: "path",
|
||||
},
|
||||
example: "3b555052-a960-4301-8d38-a6f1acb98dbe",
|
||||
}),
|
||||
});
|
||||
|
||||
const Body = z.object({
|
||||
comment: z
|
||||
.string()
|
||||
.openapi({ example: "Reason to why i had a badd adjustment." }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["logistics"],
|
||||
summary: "Post a comment to why you had a discrepancy",
|
||||
method: "post",
|
||||
path: "/postcomment/:adjId",
|
||||
middleware: authMiddleware,
|
||||
request: {
|
||||
params: ParamsSchema,
|
||||
body: { content: { "application/json": { schema: Body } } },
|
||||
},
|
||||
// description:
|
||||
// "Creates a silo adjustment for the silo if and stores the stock numbers.",
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c: any) => {
|
||||
//apiHit(c, { endpoint: "api/sqlProd/close" });
|
||||
const authHeader = c.req.header("Authorization");
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
const { adjId } = c.req.valid("param");
|
||||
|
||||
try {
|
||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
try {
|
||||
//return apiReturn(c, true, access?.message, access?.data, 200);
|
||||
const data = await c.req.json();
|
||||
|
||||
const addComment = await postSiloComment(
|
||||
adjId,
|
||||
data.comment,
|
||||
data.key,
|
||||
payload.user
|
||||
);
|
||||
return c.json(
|
||||
{
|
||||
success: addComment.success,
|
||||
message: addComment.message,
|
||||
data: addComment.data,
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "Missing data please try again",
|
||||
error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
return c.json({ success: false, message: "Unauthorized" }, 401);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
@@ -0,0 +1,41 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
{{!--<title>Order Summary</title> --}}
|
||||
{{> styles}}
|
||||
<style>
|
||||
pre {
|
||||
background-color: #f8f9fa;
|
||||
color: #d63384;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
white-space: pre-wrap;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
{{!-- <link rel="stylesheet" href="styles/styles.css" /> --}}
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
{{greeting}},<br/><br/>
|
||||
|
||||
A silo adjustment was just completed on {{siloName}}, with a variation of {{variance}}.<br/><br/>
|
||||
|
||||
The data that was passed over.<br/><br/>
|
||||
Current stock at the time of the adjustment: {{currentLevel}}.<br/><br/>
|
||||
|
||||
What was entered as the new number: {{newLevel}}<br/><br/>
|
||||
|
||||
Please add your comment as to why the variance greater than {{variancePer}}<br/><br/>
|
||||
|
||||
<a href="http://{{server}}:5173/siloAdjustments/comment/{{adjustID}}"
|
||||
style="display:inline-block; padding:10px 20px; text-decoration:none; border-radius:5px;">
|
||||
Add a Comment
|
||||
</a><br/><br/>
|
||||
Best regards,<br/><br/>
|
||||
LST team<br/>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
73
server/services/server/route/modules/getSubModules.ts
Normal file
73
server/services/server/route/modules/getSubModules.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { modules } from "../../../../../database/schema/modules.js";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { subModules } from "../../../../../database/schema/subModules.js";
|
||||
|
||||
// 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().optional(),
|
||||
module_id: z
|
||||
.string()
|
||||
.openapi({ example: "6c922c6c-7de3-4ec4-acb0-f068abdc" })
|
||||
.optional(),
|
||||
name: z.string().openapi({ example: "Production" }).optional(),
|
||||
active: z.boolean().openapi({ example: true }).optional(),
|
||||
roles: z
|
||||
.string()
|
||||
.openapi({ example: `["viewer","technician"]` })
|
||||
.optional(),
|
||||
});
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["server"],
|
||||
summary: "Returns all submodules in the server",
|
||||
method: "get",
|
||||
path: "/submodules",
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": { schema: responseSchema },
|
||||
},
|
||||
description: "Response message",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
//console.log("system modules");
|
||||
let module: any = [];
|
||||
try {
|
||||
module = await db.select().from(subModules); // .where(eq(modules.active, true));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
module = [];
|
||||
}
|
||||
|
||||
// parse the roles
|
||||
const updateModules = module.map((m: any) => {
|
||||
if (m.roles) {
|
||||
return { ...m, roles: m?.roles };
|
||||
}
|
||||
return m;
|
||||
}); //JSON.parse(module[0]?.roles);
|
||||
|
||||
// Return response with the received data
|
||||
|
||||
return c.json({
|
||||
message: `All active submodules`,
|
||||
data: module,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
@@ -15,6 +15,7 @@ import updateServer from "./route/updates/updateServer.js";
|
||||
import { setPerms } from "./utils/testServerPerms.js";
|
||||
import serviceControl from "./route/servers/serverContorl.js";
|
||||
import { areSubModulesIn } from "./utils/subModuleCheck.js";
|
||||
import getSubmodules from "./route/modules/getSubModules.js";
|
||||
|
||||
// making sure all modules are in properly
|
||||
setTimeout(async () => {
|
||||
@@ -31,6 +32,7 @@ const routes = [
|
||||
getModules,
|
||||
updateModule,
|
||||
addModule,
|
||||
getSubmodules,
|
||||
// settings
|
||||
addSetting,
|
||||
getSettings,
|
||||
|
||||
@@ -12,7 +12,7 @@ const newSubModules = [
|
||||
name: "Silo Adjustmnet",
|
||||
moduleName: "logistics",
|
||||
description: "Do a silo adjustmnet",
|
||||
link: "/sa",
|
||||
link: "/siloAdjustments",
|
||||
icon: "Cylinder",
|
||||
active: false,
|
||||
roles: ["tester", "systemAdmin"],
|
||||
@@ -156,6 +156,7 @@ export const areSubModulesIn = async () => {
|
||||
roles: newSubModules[i].roles,
|
||||
link: newSubModules[i].link,
|
||||
subSubModule: newSubModules[i].subSubModule,
|
||||
icon: newSubModules[i].icon,
|
||||
},
|
||||
}) // this will only update the ones that are new :D
|
||||
.returning({ name: subModules.name });
|
||||
@@ -167,6 +168,7 @@ export const areSubModulesIn = async () => {
|
||||
"SubModules were just added due to missing them on server startup"
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
|
||||
26
server/services/sqlServer/querys/silo/siloQuery.ts
Normal file
26
server/services/sqlServer/querys/silo/siloQuery.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export const siloQuery = `
|
||||
SELECT
|
||||
V_LagerAbteilungen.Bezeichnung AS Description,
|
||||
V_LagerAbteilungen.IdWarenLager AS WarehouseID,
|
||||
V_LagerAbteilungen.IdLagerAbteilung AS LocationID,
|
||||
ROUND(SUM(einlagerungsmengesum), 2) AS Stock_Total,
|
||||
COALESCE(LastAdjustment, '1900-01-01') AS LastAdjustment
|
||||
FROM AlplaPROD_test1.dbo.V_LagerAbteilungen (NOLOCK)
|
||||
JOIN
|
||||
AlplaPROD_test1.dbo.V_LagerPositionenBarcodes ON
|
||||
AlplaPROD_test1.dbo.V_LagerAbteilungen.IdLagerAbteilung =
|
||||
AlplaPROD_test1.dbo.V_LagerPositionenBarcodes.IdLagerAbteilung
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
IdLagerAbteilung,
|
||||
MAX(CASE WHEN CONVERT(CHAR(10), Buchungsdatum, 120) IS NULL THEN '1900-01-01' ELSE CONVERT(CHAR(10), Buchungsdatum, 120) END) AS LastAdjustment
|
||||
FROM AlplaPROD_test1.dbo.V_LagerBuchungen (NOLOCK)
|
||||
WHERE urheber = 2900
|
||||
GROUP BY IdLagerAbteilung
|
||||
) AS LastAdj ON AlplaPROD_test1.dbo.V_LagerAbteilungen.IdLagerAbteilung = LastAdj.IdLagerAbteilung
|
||||
WHERE materialsilo = 1
|
||||
AND aktiv = 1
|
||||
|
||||
GROUP BY V_LagerAbteilungen.Bezeichnung, V_LagerAbteilungen.IdWarenLager, V_LagerAbteilungen.IdLagerAbteilung, LastAdjustment
|
||||
ORDER BY V_LagerAbteilungen.Bezeichnung
|
||||
`;
|
||||
Reference in New Issue
Block a user