gopalswami's picture
Refactor invoice handling:
2041c17
import {
DataTypes,
Model,
CreationOptional,
ValidationErrorItem,
Op,
ValidationError,
} from "sequelize";
import { sequelize } from "./index";
import { InvoiceInterface } from "../shared/interfaces/invoice.interface";
import User from "./users";
import InvoiceDetail from "./invoicedetail";
class Invoice extends Model<InvoiceInterface> implements InvoiceInterface {
declare id?: number;
declare reference_number: string;
declare invoice_number: string;
declare vendor_name: string;
declare invoice_date: Date;
declare total: number;
declare amount_paid: number;
declare due_date?: Date;
declare term?: string;
declare description?: string;
declare payment_status?: string;
declare pw_work_order_id?: number;
declare pw_vendor_id?: number;
declare filename?: string;
declare pdf_url?: string;
declare status: string;
declare uploaded_by: number;
declare InvoiceDetails: InvoiceDetail[];
}
Invoice.init(
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
reference_number: {
type: DataTypes.STRING(100),
},
invoice_number: {
type: DataTypes.STRING(100),
},
vendor_name: {
type: DataTypes.STRING(255),
},
invoice_date: {
type: DataTypes.DATE,
},
total: {
type: DataTypes.DECIMAL(10, 2),
},
amount_paid: {
type: DataTypes.DECIMAL(10, 2),
},
due_date: {
type: DataTypes.DATE,
allowNull: true,
},
term: {
type: DataTypes.STRING(50),
allowNull: true,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
payment_status: {
type: DataTypes.STRING(50),
defaultValue: "pending",
},
pw_work_order_id: {
type: DataTypes.INTEGER,
allowNull: true,
},
pw_vendor_id: {
type: DataTypes.INTEGER,
allowNull: true,
},
filename: {
type: DataTypes.STRING(255),
allowNull: false,
},
pdf_url: {
type: DataTypes.STRING(255),
allowNull: true,
},
status: {
type: DataTypes.STRING(50),
allowNull: false,
},
uploaded_by: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
sequelize,
tableName: "invoices",
underscored: true,
freezeTableName: true,
timestamps: true,
createdAt: "created_at",
updatedAt: "updated_at",
paranoid: true,
deletedAt: "deleted_at",
}
);
Invoice.belongsTo(User, { foreignKey: "uploaded_by", as: "uploadedBy" });
Invoice.hasMany(InvoiceDetail, { foreignKey: "invoice_id" });
const validateInvoice = async (invoice: Invoice) => {
const existingInvoice = await Invoice.findOne({
where: {
reference_number: invoice.reference_number,
status: { [Op.ne]: "archived" },
id: { [Op.ne]: invoice.id },
},
});
const validationErrorItems: ValidationErrorItem[] = [];
if (existingInvoice) {
validationErrorItems.push(
new ValidationErrorItem(
"Invoice with the same reference number already exists; archive it before adding another.",
"unique violation",
"reference_number",
invoice.reference_number,
invoice,
"isUnique",
"reference_number",
[]
)
);
}
if (validationErrorItems.length > 0) {
throw new ValidationError("Validation Error", validationErrorItems);
}
};
Invoice.addHook("beforeCreate", validateInvoice);
Invoice.addHook("beforeUpdate", validateInvoice);
Invoice.addHook("beforeSave", validateInvoice);
export default Invoice;