import type { CustomerModel } from "@erp/customers/api"; import { type CreationOptional, DataTypes, type InferAttributes, type InferCreationAttributes, Model, type NonAttribute, type Sequelize, } from "sequelize"; import type { CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemModel, } from "./customer-invoice-item.model"; import type { CustomerInvoiceTaxCreationAttributes, CustomerInvoiceTaxModel, } from "./customer-invoice-tax.model"; import type { VerifactuRecordModel } from "./verifactu-record.model"; export type CustomerInvoiceCreationAttributes = InferCreationAttributes< CustomerInvoiceModel, { omit: "items" | "taxes" | "current_customer" } > & { items?: CustomerInvoiceItemCreationAttributes[]; taxes?: CustomerInvoiceTaxCreationAttributes[]; }; export class CustomerInvoiceModel extends Model< InferAttributes, InferCreationAttributes > { // Version // declare version: CreationOptional; declare id: string; declare company_id: string; // status === issued <- (si is_proforma === true) => Es una proforma (histórica) // status === issued <- (si is_proforma === false) => Factura y enviada a Veri*Factu declare is_proforma: boolean; declare status: string; declare proforma_id: CreationOptional; // ID de la proforma origen de la factura declare series: CreationOptional; declare invoice_number: CreationOptional; declare invoice_date: CreationOptional; declare operation_date: CreationOptional; declare language_code: CreationOptional; declare currency_code: CreationOptional; declare reference: CreationOptional; declare description: CreationOptional; declare notes: CreationOptional; // Método de pago declare payment_method_id: CreationOptional; declare payment_method_description: CreationOptional; // Subtotal declare subtotal_amount_value: number; declare subtotal_amount_scale: number; // Discount percentage declare discount_percentage_value: number; declare discount_percentage_scale: number; // Discount amount declare discount_amount_value: number; declare discount_amount_scale: number; // Taxable amount (base imponible) declare taxable_amount_value: number; declare taxable_amount_scale: number; // Total taxes amount / taxes total declare taxes_amount_value: number; declare taxes_amount_scale: number; // Total declare total_amount_value: number; declare total_amount_scale: number; // Customer declare customer_id: string; declare customer_tin: CreationOptional; declare customer_name: CreationOptional; declare customer_street: CreationOptional; declare customer_street2: CreationOptional; declare customer_city: CreationOptional; declare customer_province: CreationOptional; declare customer_postal_code: CreationOptional; declare customer_country: CreationOptional; // FactuGES declare factuges_id: CreationOptional; // Relaciones declare items: NonAttribute; declare taxes: NonAttribute; declare current_customer: NonAttribute; declare verifactu: NonAttribute; static associate(database: Sequelize) { const models = database.models; const requiredModels = [ "CustomerInvoiceModel", "CustomerInvoiceItemModel", "CustomerModel", "CustomerInvoiceTaxModel", "VerifactuRecordModel", ]; // Comprobamos que los modelos existan for (const name of requiredModels) { if (!models[name]) { throw new Error(`[CustomerInvoiceModel.associate] Missing model: ${name}`); } } // Los modelos existen const { CustomerInvoiceModel, CustomerInvoiceItemModel, CustomerModel, CustomerInvoiceTaxModel, VerifactuRecordModel, } = models; CustomerInvoiceModel.hasOne(VerifactuRecordModel, { as: "verifactu", foreignKey: "invoice_id", }); CustomerInvoiceModel.belongsTo(CustomerModel, { as: "current_customer", foreignKey: "customer_id", constraints: false, }); CustomerInvoiceModel.hasMany(CustomerInvoiceItemModel, { as: "items", foreignKey: "invoice_id", sourceKey: "id", constraints: true, onDelete: "CASCADE", onUpdate: "CASCADE", }); CustomerInvoiceModel.hasMany(CustomerInvoiceTaxModel, { as: "taxes", foreignKey: "invoice_id", sourceKey: "id", constraints: true, onDelete: "CASCADE", onUpdate: "CASCADE", }); } static hooks(_database: Sequelize) { // } } export default (database: Sequelize) => { CustomerInvoiceModel.init( { /*version: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, defaultValue: 0, },*/ id: { type: DataTypes.UUID, primaryKey: true, }, company_id: { type: DataTypes.UUID, allowNull: false, }, is_proforma: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true, }, proforma_id: { type: DataTypes.UUID, allowNull: true, defaultValue: null, }, status: { type: new DataTypes.STRING(), allowNull: false, defaultValue: "draft", }, series: { type: new DataTypes.STRING(10), allowNull: true, defaultValue: null, }, invoice_number: { type: new DataTypes.STRING(12), allowNull: true, defaultValue: null, }, invoice_date: { type: new DataTypes.DATEONLY(), allowNull: false, }, operation_date: { type: new DataTypes.DATEONLY(), allowNull: true, defaultValue: null, }, language_code: { type: DataTypes.STRING(2), allowNull: false, defaultValue: "es", }, currency_code: { type: new DataTypes.STRING(3), allowNull: false, defaultValue: "EUR", }, reference: { type: new DataTypes.STRING(), allowNull: true, defaultValue: null, }, description: { type: new DataTypes.STRING(), allowNull: true, defaultValue: null, }, notes: { type: new DataTypes.TEXT(), allowNull: true, defaultValue: null, }, payment_method_id: { type: DataTypes.UUID, allowNull: true, defaultValue: null, }, payment_method_description: { type: new DataTypes.STRING(), allowNull: true, defaultValue: null, }, subtotal_amount_value: { type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes allowNull: false, defaultValue: 0, }, subtotal_amount_scale: { type: new DataTypes.SMALLINT(), allowNull: false, defaultValue: 2, }, discount_percentage_value: { type: new DataTypes.SMALLINT(), allowNull: false, defaultValue: 0, }, discount_percentage_scale: { type: new DataTypes.SMALLINT(), allowNull: false, defaultValue: 2, }, discount_amount_value: { type: new DataTypes.BIGINT(), allowNull: false, defaultValue: 0, }, discount_amount_scale: { type: new DataTypes.SMALLINT(), allowNull: false, defaultValue: 2, }, taxable_amount_value: { type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes allowNull: false, defaultValue: 0, }, taxable_amount_scale: { type: new DataTypes.SMALLINT(), allowNull: false, defaultValue: 2, }, taxes_amount_value: { type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes allowNull: false, defaultValue: 0, }, taxes_amount_scale: { type: new DataTypes.SMALLINT(), allowNull: false, defaultValue: 2, }, total_amount_value: { type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes allowNull: false, defaultValue: 0, }, total_amount_scale: { type: new DataTypes.SMALLINT(), allowNull: false, defaultValue: 2, }, customer_id: { type: DataTypes.UUID, allowNull: false, }, customer_tin: { type: DataTypes.STRING, allowNull: true, defaultValue: null, }, customer_name: { type: DataTypes.STRING, allowNull: true, defaultValue: null, }, customer_street: { type: DataTypes.STRING, allowNull: true, defaultValue: null, }, customer_street2: { type: DataTypes.STRING, allowNull: true, defaultValue: null, }, customer_city: { type: DataTypes.STRING, allowNull: true, defaultValue: null, }, customer_province: { type: DataTypes.STRING, allowNull: true, defaultValue: null, }, customer_postal_code: { type: DataTypes.STRING, allowNull: true, defaultValue: null, }, customer_country: { type: DataTypes.STRING, allowNull: true, defaultValue: null, }, factuges_id: { type: DataTypes.STRING, allowNull: true, defaultValue: null, }, }, { sequelize: database, modelName: "CustomerInvoiceModel", tableName: "customer_invoices", underscored: true, paranoid: true, // softs deletes timestamps: true, createdAt: "created_at", updatedAt: "updated_at", deletedAt: "deleted_at", indexes: [ { name: "idx_invoices_company_date", fields: ["company_id", "deleted_at", { name: "invoice_date", order: "DESC" }], }, { name: "idx_invoice_company_series_number", fields: ["company_id", "series", "invoice_number"], unique: true, }, // <- para consulta get { name: "idx_invoice_date", fields: ["invoice_date"] }, // <- para ordenación { name: "idx_invoice_company_id", fields: ["id", "company_id"], unique: true }, // <- para consulta get { name: "idx_invoice_proforma_id", fields: ["proforma_id"], unique: false }, // <- para localizar factura por medio de proforma { name: "idx_invoice_factuges", fields: ["factuges_id"], unique: false }, // <- para el proceso python // Para búsquedas simples { name: "ft_customer_invoice", type: "FULLTEXT", fields: ["invoice_number", "reference", "description"], }, ], whereMergeStrategy: "and", // <- cómo tratar el merge de un scope defaultScope: {}, scopes: {}, /*hooks: { // Incrementa la versión en cada update exitoso (OCC). beforeUpdate: (instance) => { const current = instance.get("version") as number; instance.set("version", current + 1); }, },*/ } ); return CustomerInvoiceModel; };