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 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;