All files / src/domains/mdm/infrastructure/schemas currency.schema.ts

0% Statements 0/75
0% Branches 0/31
100% Functions 0/0
0% Lines 0/65

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151                                                                                                                                                                                                                                                                                                             
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Schema as MongooseSchema } from 'mongoose';
 
// ════════════════════════════════════════════════════════════════
// CURRENCY
// ════════════════════════════════════════════════════════════════
 
@Schema({ collection: 'mdm_currencies', timestamps: true })
export class Currency extends Document {
  @Prop({ required: true, unique: true, uppercase: true })
  currencyCode!: string; // INR, USD
  @Prop({ required: true }) currencyName!: string;
  @Prop() nativeName?: string;
  @Prop() symbol!: string; // ₹, $
  @Prop() narrowSymbol?: string; // ₹ (same often)
  @Prop({ required: true, default: 2 }) minorUnits!: number; // decimal places
  @Prop({ required: true, default: 2 }) roundingPrecision!: number;
  @Prop({ enum: ['standard', 'none', '5', '10'] }) roundingIncrement?: string;
  @Prop() numericCode?: string; // ISO 4217 numeric
  @Prop({ type: [String], default: [] }) countryCodes!: string[];
  @Prop({ default: false }) crypto!: boolean;
  @Prop({ default: true }) active!: boolean;
  @Prop({ default: false }) systemManaged!: boolean;
}
export const CurrencySchema = SchemaFactory.createForClass(Currency);
CurrencySchema.index({ currencyCode: 1 }, { unique: true });
CurrencySchema.index({ active: 1 });
CurrencySchema.index({ countryCodes: 1 });
 
// ════════════════════════════════════════════════════════════════
// EXCHANGE RATE PROVIDER
// ════════════════════════════════════════════════════════════════
 
@Schema({ collection: 'mdm_exchange_rate_providers', timestamps: true })
export class ExchangeRateProvider extends Document {
  @Prop({ required: true, unique: true }) providerKey!: string; // open_exchange_rates, rbi_feed, mock
  @Prop({ required: true }) providerName!: string;
  @Prop({
    enum: ['live', 'daily', 'weekly', 'manual', 'mock'],
    default: 'manual',
  })
  updateFrequency!: string;
  @Prop({ default: true }) active!: boolean;
  @Prop({ default: false }) isPrimary!: boolean;
  @Prop({ type: MongooseSchema.Types.Mixed }) config?: any; // API keys injected from env, base URL, etc.
}
export const ExchangeRateProviderSchema =
  SchemaFactory.createForClass(ExchangeRateProvider);
ExchangeRateProviderSchema.index({ providerKey: 1 }, { unique: true });
 
// ════════════════════════════════════════════════════════════════
// CURRENCY EXCHANGE RATE
// Point-in-time rate from one currency to another
// ════════════════════════════════════════════════════════════════
 
@Schema({ collection: 'mdm_exchange_rates', timestamps: true })
export class CurrencyExchangeRate extends Document {
  @Prop({ required: true, uppercase: true }) fromCurrency!: string;
  @Prop({ required: true, uppercase: true }) toCurrency!: string;
  @Prop({ required: true }) rateDate!: Date;
  // Rate stored as string to preserve decimal precision (no floating-point)
  @Prop({ required: true }) rate!: string; // e.g. "83.47"
  @Prop() bidRate?: string;
  @Prop() askRate?: string;
  @Prop() midRate?: string;
  @Prop({
    required: true,
    enum: ['spot', 'official', 'bank', 'market', 'custom', 'mock'],
    default: 'custom',
  })
  rateType!: string;
  @Prop({ required: true }) providerKey!: string;
  @Prop({
    required: true,
    enum: ['active', 'superseded', 'reverted'],
    default: 'active',
  })
  status!: string;
  @Prop({ type: MongooseSchema.Types.ObjectId })
  tenantId?: MongooseSchema.Types.ObjectId; // null = global rate
}
export const CurrencyExchangeRateSchema =
  SchemaFactory.createForClass(CurrencyExchangeRate);
CurrencyExchangeRateSchema.index({
  fromCurrency: 1,
  toCurrency: 1,
  rateDate: -1,
  rateType: 1,
});
CurrencyExchangeRateSchema.index({
  fromCurrency: 1,
  toCurrency: 1,
  status: 1,
  rateDate: -1,
});
CurrencyExchangeRateSchema.index({
  tenantId: 1,
  fromCurrency: 1,
  toCurrency: 1,
  rateDate: -1,
});
 
// ════════════════════════════════════════════════════════════════
// EXCHANGE RATE IMPORT
// Tracks bulk import jobs of exchange rates
// ════════════════════════════════════════════════════════════════
 
@Schema({ collection: 'mdm_exchange_rate_imports', timestamps: true })
export class ExchangeRateImport extends Document {
  @Prop({ required: true }) providerKey!: string;
  @Prop({ required: true }) importDate!: Date;
  @Prop({
    required: true,
    enum: ['queued', 'processing', 'completed', 'partial', 'failed'],
    default: 'queued',
  })
  status!: string;
  @Prop({ default: 0 }) totalRecords!: number;
  @Prop({ default: 0 }) processedRecords!: number;
  @Prop({ default: 0 }) failedRecords!: number;
  @Prop({ type: [String], default: [] }) importErrors!: string[];
  @Prop({ type: MongooseSchema.Types.ObjectId })
  importedBy?: MongooseSchema.Types.ObjectId;
}
export const ExchangeRateImportSchema =
  SchemaFactory.createForClass(ExchangeRateImport);
ExchangeRateImportSchema.index({ providerKey: 1, importDate: -1 });
ExchangeRateImportSchema.index({ status: 1 });
 
// ════════════════════════════════════════════════════════════════
// EXCHANGE RATE SNAPSHOT
// EOD consolidated snapshot per tenant/period (for reporting)
// ════════════════════════════════════════════════════════════════
 
@Schema({ collection: 'mdm_exchange_rate_snapshots', timestamps: true })
export class ExchangeRateSnapshot extends Document {
  @Prop({ required: true }) snapshotDate!: Date; // YYYY-MM-DD
  @Prop({ required: true }) baseCurrency!: string;
  @Prop({ type: MongooseSchema.Types.Mixed }) rates!: Record<string, string>; // { USD: "1", INR: "83.47", ... }
  @Prop() providerKey?: string;
  @Prop({ type: MongooseSchema.Types.ObjectId })
  tenantId?: MongooseSchema.Types.ObjectId;
}
export const ExchangeRateSnapshotSchema =
  SchemaFactory.createForClass(ExchangeRateSnapshot);
ExchangeRateSnapshotSchema.index({
  snapshotDate: -1,
  baseCurrency: 1,
  tenantId: 1,
});