feat(invoices): added invoice + linking to forklift
This commit is contained in:
160
app/src/internal/forklifts/routes/invoices/addInvoice.ts
Normal file
160
app/src/internal/forklifts/routes/invoices/addInvoice.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import axios from "axios";
|
||||
import { type DrizzleError, sql } from "drizzle-orm";
|
||||
import type { Request, Response } from "express";
|
||||
import { Router } from "express";
|
||||
import https from "https";
|
||||
import type z from "zod";
|
||||
import { db } from "../../../../pkg/db/db.js";
|
||||
import {
|
||||
leaseInvoiceForklifts,
|
||||
newForkliftInvoiceSchema,
|
||||
} from "../../../../pkg/db/schema/forkliftLeasesInvoice.js";
|
||||
import {
|
||||
leaseInvoices,
|
||||
newInvoiceSchema,
|
||||
} from "../../../../pkg/db/schema/leaseInvoices.js";
|
||||
import { createLogger } from "../../../../pkg/logger/logger.js";
|
||||
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", async (req: Request, res: Response) => {
|
||||
// when a new server is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there
|
||||
//res.status(200).json({ message: "Server added", ip: req.hostname });
|
||||
const log = createLogger({ module: "forklift", subModule: "add invoice" });
|
||||
|
||||
const parsedInvoice = newInvoiceSchema.safeParse({
|
||||
leaseId: req.body.leaseId,
|
||||
companyId: req.body.companyId,
|
||||
invoiceNumber: req.body.invoiceNumber,
|
||||
invoiceDate: req.body.invoiceDate,
|
||||
uploadedBy: req.body.uploadedBy,
|
||||
totalAmount: req.body.totalAmount,
|
||||
});
|
||||
|
||||
if (!parsedInvoice.success)
|
||||
return res.status(400).json({ error: parsedInvoice.error.flatten });
|
||||
|
||||
const invoiceData = parsedInvoice.data;
|
||||
|
||||
const forkliftItems = Array.isArray(req.body.forklifts)
|
||||
? req.body.forklifts
|
||||
: [];
|
||||
|
||||
const validatedForklifts = []; //z.infer<typeof newForkliftInvoiceSchema>[] = [];
|
||||
|
||||
for (const item of forkliftItems) {
|
||||
// const parsedItem = newForkliftInvoiceSchema.safeParse(item);
|
||||
// if (parsedItem.success) {
|
||||
validatedForklifts.push(item);
|
||||
//} else {
|
||||
//return res.status(400).json({ error: parsedItem.error.flatten() });
|
||||
//}
|
||||
}
|
||||
|
||||
// this will be the total invoice amount minus each forklift this way we can keep the total amount in here plus forklifts seperated
|
||||
const totalAmount = (
|
||||
validatedForklifts.reduce((sum, f) => sum + Number(f.amount || 0), 0) -
|
||||
req.body.totalInvoice
|
||||
).toString();
|
||||
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.insert(leaseInvoices)
|
||||
.values({
|
||||
...invoiceData,
|
||||
uploadedBy: req.user!.username || "lst_user",
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: leaseInvoices.invoiceNumber,
|
||||
set: {
|
||||
totalAmount,
|
||||
invoiceDate: invoiceData.invoiceDate,
|
||||
uploadedBy: req.user!.username || "lst_user",
|
||||
},
|
||||
})
|
||||
.returning(),
|
||||
);
|
||||
|
||||
if (error) {
|
||||
const err: DrizzleError = error;
|
||||
return res.status(400).json({
|
||||
message: `Error adding lease`,
|
||||
error: err.cause,
|
||||
});
|
||||
}
|
||||
|
||||
const invoiceId = data[0]?.id;
|
||||
|
||||
const forkliftInvoices = validatedForklifts.map((f) => ({
|
||||
invoiceId,
|
||||
forkliftId: f.forklift_Id,
|
||||
amount: f.amount,
|
||||
}));
|
||||
console.log(forkliftInvoices);
|
||||
if (validatedForklifts.length > 0) {
|
||||
await db.insert(leaseInvoiceForklifts).values(forkliftInvoices);
|
||||
// .onConflictDoUpdate({
|
||||
// target: [
|
||||
// leaseInvoiceForklifts.invoiceId,
|
||||
// leaseInvoiceForklifts.forkliftId,
|
||||
// ],
|
||||
// set: { amount: (excluded) => excluded.amount },
|
||||
// });
|
||||
}
|
||||
// if (req.hostname === "localhost" && process.env.MAIN_SERVER) {
|
||||
// log.info({}, "Running in dev server about to add in a new server");
|
||||
// const axiosInstance = axios.create({
|
||||
// httpsAgent: new https.Agent({ rejectUnauthorized: false }),
|
||||
// baseURL: process.env.MAIN_SERVER, // e.g. "https://example.com"
|
||||
// withCredentials: true,
|
||||
// });
|
||||
|
||||
// const loginRes = (await axiosInstance.post(
|
||||
// `${process.env.MAIN_SERVER}/lst/api/auth/sign-in/username`,
|
||||
// {
|
||||
// username: process.env.MAIN_SERVER_USERNAME,
|
||||
// password: process.env.MAIN_SERVER_PASSWORD,
|
||||
// },
|
||||
// {
|
||||
// headers: { "Content-Type": "application/json" },
|
||||
// },
|
||||
// )) as any;
|
||||
// const setCookie = loginRes.headers["set-cookie"][0];
|
||||
|
||||
// if (!setCookie) {
|
||||
// throw new Error("Did not receive a Set-Cookie header from login");
|
||||
// }
|
||||
|
||||
// const { data, error } = await tryCatch(
|
||||
// axios.post(
|
||||
// `${process.env.MAIN_SERVER}/lst/api/forklifts/leases`,
|
||||
// parsed.data,
|
||||
// {
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// Cookie: setCookie.split(";")[0],
|
||||
// },
|
||||
// withCredentials: true,
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
|
||||
// if (error) {
|
||||
// log.error(
|
||||
// { stack: error },
|
||||
// "There was an error adding the company to Main Server",
|
||||
// );
|
||||
// }
|
||||
// log.info(
|
||||
// { stack: data?.data },
|
||||
// "A new Company was just added to the server.",
|
||||
// );
|
||||
// }
|
||||
|
||||
return res
|
||||
.status(201)
|
||||
.json({ message: `lease ${data[0]?.invoiceNumber} added`, data: data });
|
||||
});
|
||||
|
||||
export default router;
|
||||
52
app/src/internal/forklifts/routes/invoices/getInvoices.ts
Normal file
52
app/src/internal/forklifts/routes/invoices/getInvoices.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { and, asc, eq, relations } from "drizzle-orm";
|
||||
import type { Request, Response } from "express";
|
||||
import { Router } from "express";
|
||||
import { db } from "../../../../pkg/db/db.js";
|
||||
import { forkliftCompanies } from "../../../../pkg/db/schema/forkliftLeaseCompanys.js";
|
||||
import { leases } from "../../../../pkg/db/schema/forkliftLeases.js";
|
||||
import { forklifts } from "../../../../pkg/db/schema/forklifts.js";
|
||||
import { leaseInvoices } from "../../../../pkg/db/schema/leaseInvoices.js";
|
||||
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
const invoiceNumber = req.query.lease;
|
||||
|
||||
const conditions = [];
|
||||
|
||||
if (invoiceNumber !== undefined) {
|
||||
conditions.push(eq(leaseInvoices.invoiceNumber, `${invoiceNumber}`));
|
||||
}
|
||||
|
||||
//conditions.push(eq(forkliftCompanies.active, true));
|
||||
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.select(
|
||||
// {
|
||||
// id: leases.id,
|
||||
// leaseNumber: leases.leaseNumber,
|
||||
// startDate: leases.startDate,
|
||||
// endDate: leases.endDate,
|
||||
// leaseLink: leases.leaseLink,
|
||||
// companyName: forkliftCompanies.name,
|
||||
// add_user: leases.add_user,
|
||||
// add_date: leases.add_date,
|
||||
// upd_user: leases.upd_user,
|
||||
// upd_date: leases.upd_date,
|
||||
// }
|
||||
)
|
||||
.from(leaseInvoices)
|
||||
//.innerJoin(forkliftCompanies, eq(forkliftCompanies.id, leases.companyId))
|
||||
.where(and(...conditions))
|
||||
.orderBy(asc(leaseInvoices.invoiceNumber)),
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json({ error: error });
|
||||
}
|
||||
res.status(200).json({ message: "Current Leases", data: data });
|
||||
});
|
||||
|
||||
export default router;
|
||||
22
app/src/internal/forklifts/routes/invoices/invoiceRoutes.ts
Normal file
22
app/src/internal/forklifts/routes/invoices/invoiceRoutes.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Router } from "express";
|
||||
import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js";
|
||||
|
||||
import addInvoice from "./addInvoice.js";
|
||||
import getInvoices from "./getInvoices.js";
|
||||
import updateInvoice from "./updateInvoices.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use(
|
||||
"/",
|
||||
requireAuth("forklifts", ["systemAdmin", "admin", "manager"]),
|
||||
getInvoices,
|
||||
);
|
||||
router.use("/", requireAuth("forklifts", ["systemAdmin", "admin"]), addInvoice);
|
||||
router.use(
|
||||
"/",
|
||||
requireAuth("forklifts", ["systemAdmin", "admin"]),
|
||||
updateInvoice,
|
||||
);
|
||||
|
||||
export default router;
|
||||
114
app/src/internal/forklifts/routes/invoices/updateInvoices.ts
Normal file
114
app/src/internal/forklifts/routes/invoices/updateInvoices.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import axios from "axios";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import type { Request, Response } from "express";
|
||||
import { Router } from "express";
|
||||
import https from "https";
|
||||
import { db } from "../../../../pkg/db/db.js";
|
||||
import { forkliftCompanies } from "../../../../pkg/db/schema/forkliftLeaseCompanys.js";
|
||||
import { leases } from "../../../../pkg/db/schema/forkliftLeases.js";
|
||||
import { serverData } from "../../../../pkg/db/schema/servers.js";
|
||||
import { createLogger } from "../../../../pkg/logger/logger.js";
|
||||
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.patch("/:id", async (req: Request, res: Response) => {
|
||||
const log = createLogger({
|
||||
module: "forklifts",
|
||||
subModule: "update invoice",
|
||||
});
|
||||
|
||||
// when a server is updated and is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there, we want to insert with update on conflict.
|
||||
const id = req.params.id;
|
||||
const updates: Record<string, any> = {};
|
||||
console.log(req.body);
|
||||
if (req.body?.leaseNumber !== undefined) {
|
||||
updates.leaseNumber = req.body.leaseNumber;
|
||||
}
|
||||
|
||||
if (req.body?.startDate !== undefined) {
|
||||
updates.startDate = req.body.startDate;
|
||||
}
|
||||
|
||||
if (req.body?.endDate !== undefined) {
|
||||
updates.endDate = req.body.endDate;
|
||||
}
|
||||
|
||||
if (req.body?.companyId !== undefined) {
|
||||
updates.companyId = req.body.companyId;
|
||||
}
|
||||
|
||||
if (req.body?.leaseLink !== undefined) {
|
||||
updates.leaseLink = req.body.leaseLink;
|
||||
}
|
||||
|
||||
updates.upd_user = req.user!.username || "lst_user";
|
||||
updates.upd_date = sql`NOW()`;
|
||||
|
||||
console.log(updates);
|
||||
try {
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await db.update(leases).set(updates).where(eq(leases.id, id));
|
||||
}
|
||||
|
||||
// if (req.hostname === "localhost" && process.env.MAIN_SERVER) {
|
||||
// log.info({}, "Running in dev server about to add in a new server");
|
||||
// const axiosInstance = axios.create({
|
||||
// httpsAgent: new https.Agent({ rejectUnauthorized: false }),
|
||||
// baseURL: process.env.MAIN_SERVER,
|
||||
// withCredentials: true,
|
||||
// });
|
||||
|
||||
// const loginRes = (await axiosInstance.post(
|
||||
// `${process.env.MAIN_SERVER}/lst/api/auth/sign-in/username`,
|
||||
// {
|
||||
// username: process.env.MAIN_SERVER_USERNAME,
|
||||
// password: process.env.MAIN_SERVER_PASSWORD,
|
||||
// },
|
||||
// {
|
||||
// headers: { "Content-Type": "application/json" },
|
||||
// },
|
||||
// )) as any;
|
||||
|
||||
// const setCookie = loginRes?.headers["set-cookie"][0];
|
||||
|
||||
// //console.log(setCookie.split(";")[0].replace("__Secure-", ""));
|
||||
|
||||
// if (!setCookie) {
|
||||
// throw new Error("Did not receive a Set-Cookie header from login");
|
||||
// }
|
||||
|
||||
// const { data, error } = await tryCatch(
|
||||
// axios.patch(
|
||||
// `${process.env.MAIN_SERVER}/lst/api/forklifts/leases/${id}`,
|
||||
// updates,
|
||||
// {
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// Cookie: setCookie.split(";")[0],
|
||||
// },
|
||||
// withCredentials: true,
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
|
||||
// if (error) {
|
||||
// //console.log(error);
|
||||
// log.error(
|
||||
// { stack: error },
|
||||
// "There was an error updating the lease to Main Server",
|
||||
// );
|
||||
// }
|
||||
// log.info(
|
||||
// { stack: data?.data },
|
||||
// "A new lease was just updated to the server.",
|
||||
// );
|
||||
// }
|
||||
res.status(200).json({ message: `${id} was just updated` });
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
res.status(200).json({ message: "Error updating lease", error });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,9 +1,10 @@
|
||||
import { numeric, pgTable, serial, uuid } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { forklifts } from "./forklifts.js";
|
||||
import { leaseInvoices } from "./leaseInvoices.js";
|
||||
|
||||
export const leaseInvoiceForklifts = pgTable("lease_invoice_forklifts", {
|
||||
id: serial("id").primaryKey(),
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
invoiceId: uuid("invoice_id")
|
||||
.notNull()
|
||||
.references(() => leaseInvoices.id, { onDelete: "cascade" }),
|
||||
@@ -12,3 +13,7 @@ export const leaseInvoiceForklifts = pgTable("lease_invoice_forklifts", {
|
||||
.references(() => forklifts.forklift_id, { onDelete: "cascade" }),
|
||||
amount: numeric("amount"), // optional: amount of invoice allocated to this lift
|
||||
});
|
||||
|
||||
export const newForkliftInvoiceSchema = createInsertSchema(
|
||||
leaseInvoiceForklifts,
|
||||
);
|
||||
|
||||
@@ -23,7 +23,7 @@ const status = pgEnum("forklift_status", [
|
||||
export const forklifts = pgTable("forklifts", {
|
||||
forklift_id: uuid("forklift_id").defaultRandom().primaryKey(),
|
||||
forkliftNumber: serial("forklift_number").notNull(),
|
||||
serialNumber: text("serial_number").notNull(),
|
||||
serialNumber: text("serial_number").unique().notNull(),
|
||||
model: text("model").notNull(),
|
||||
plant: text("plant")
|
||||
.notNull()
|
||||
@@ -41,8 +41,8 @@ export const forklifts = pgTable("forklifts", {
|
||||
dataPlate: text("data_plate"),
|
||||
add_date: timestamp("add_date").defaultNow(),
|
||||
add_user: text("add_user").default("LST"),
|
||||
upd_date: timestamp("add_date").defaultNow(),
|
||||
upd_user: text("add_user").default("LST"),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
upd_user: text("upd_user").default("LST"),
|
||||
});
|
||||
|
||||
export const forkliftsSchema = createSelectSchema(forklifts);
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import { date, numeric, pgTable, text, uuid } from "drizzle-orm/pg-core";
|
||||
import { forkliftCompanies } from "./forkliftLeaseCompanys.js";
|
||||
import {
|
||||
date,
|
||||
numeric,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { leases } from "./forkliftLeases.js";
|
||||
import { forklifts } from "./forklifts.js";
|
||||
|
||||
export const leaseInvoices = pgTable("lease_invoices", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
leaseId: uuid("lease_id")
|
||||
.notNull()
|
||||
.references(() => leases.id, { onDelete: "cascade" }),
|
||||
companyId: uuid("company_id").references(() => forkliftCompanies.id),
|
||||
invoiceNumber: text("invoice_number").notNull(),
|
||||
invoiceNumber: text("invoice_number").unique().notNull(),
|
||||
invoiceDate: date("invoice_date").notNull(),
|
||||
forkliftId: uuid("forklift_id")
|
||||
.notNull()
|
||||
.references(() => forklifts.forklift_id, { onDelete: "cascade" }),
|
||||
totalAmount: numeric("total_amount"),
|
||||
add_date: timestamp("add_date"),
|
||||
uploadedBy: text("uploaded_by"),
|
||||
});
|
||||
|
||||
export const newInvoiceSchema = createInsertSchema(leaseInvoices);
|
||||
|
||||
Reference in New Issue
Block a user