Open geekelo opened 1 month ago
- In profiles controller
@Get(':id/escrow_amount')
@UsePipes(ValidationPipe)
async getUserEscrowAmount(@Param('id') id: string) {
try {
const profile = await this.service.findOne({ where: { id: id } });
if (!profile) {
throw new NotFoundException('Profile ID Not Found.');
}
return Generic.response(1, {
escrowAmount: profile.escrowAmount,
tradeEscrowAmount: profile.tradeEscrowAmount,
});
} catch (error) {
throw new HttpException(error.message, error.status);
}
}
- In Entities
@Column({ type: 'decimal', default: 0 })
escrowAmount: number;
@Column({ type: 'decimal', default: 0 })
tradeEscrowAmount: number;
@Post()
@UsePipes(ValidationPipe)
async create(@Body() createDto: CreateOfferDto) {
try {
const pmTableData = await this.pmService.findOne({
where: { id: createDto.paymentMethodId },
relations: { paymentMethodGroup: true },
});
if (!pmTableData) {
throw new NotFoundException('Payment Method ID Not Found.');
}
const pmgTableData = await this.pmgService.findOne({
where: { id: pmTableData.paymentMethodGroup.id },
});
if (!pmgTableData) {
throw new NotFoundException('Payment Method Group ID Not Found.');
}
const pTableData = await this.pService.findOne({
where: { id: createDto.profileId },
relations: { country: true },
});
if (!pTableData) {
throw new NotFoundException('User ID Not Found.');
}
const ccTableData = await this.ccService.findOne({
where: { code: createDto.cryptoCurrencyCode },
});
if (!ccTableData) {
throw new NotFoundException('Crypto Currency Code Not Found.');
}
if (createDto.country) {
const cTableData = await this.cService.findOne({
where: { code: createDto.country },
});
if (!cTableData) {
throw new NotFoundException('Country Code Not Found.');
}
createDto.country = cTableData;
} else if (pTableData && pTableData.country) {
createDto.country = pTableData.country;
}
const fcTableData = await this.fcService.findOne({
where: { code: createDto.fiatCurrencyCode },
});
if (!fcTableData) {
throw new NotFoundException('Fiat Curreny Code Not Found.');
}
createDto.paymentMethod = pmTableData;
createDto.paymentMethodGroup = pmgTableData;
createDto.cryptoCurrency = ccTableData;
createDto.fiatCurrency = fcTableData;
createDto.profile = pTableData;
createDto.userCountry = pTableData.country;
createDto.paymentWindow = (
createDto.fiatCurrency.code === 'NGN' ? 0.5 : 8
).toString();
const dutyHours = createDto.dutyHours
? JSON.parse(createDto.dutyHours.toString())
: null;
delete createDto.dutyHours;
const offer = await this.service.create(createDto);
const offerAmount = createDto.fixedPrice || createDto.rangeMax || 0;
pTableData.escrowAmount += offerAmount;
await this.pService.update(pTableData.id, pTableData);
const self = this;
if (dutyHours) {
dutyHours.forEach(function (row: any) {
row.offer = offer;
self.service.ceateDutyHours(row);
});
}
return Generic.response(1, await this.service.findOne(offer.id));
} catch (error) {
throw new HttpException(error.message, error.status);
}
}
@Get()
async findAll(@Query() query: GetOfferDto) {
try {
return Generic.response(
1,
await this.service.paginate(
{ page: query.query, limit: query.limit },
query,
),
);
} catch (error) {
throw new HttpException(error.message, error.status);
}
}
To refactor the current escrow amount from trades, subtracting the value of the trade amount from the escrow amount, and adding it to the trade escrow amount when a trade is completed, the relevant section of the code is in the tradeCompleted
method of the TradeService
class.
Here's the existing tradeCompleted
method with comments indicating where you need to modify the code to achieve this:
async tradeCompleted(tradeId: string, body: any) {
const trade = await this.repository.findOne({ where: { id: tradeId } });
if (!trade) {
throw new NotFoundException('Trade has been deleted or invalid!');
}
if (
![TradeStates.PAYMENT_ACCEPTED, TradeStates.COMPLETED].includes(
trade.state,
)
) {
throw new HttpException(
'You are not allowed to perform this action!',
HttpStatus.BAD_REQUEST,
);
}
const previousState = trade.state;
trade.state = TradeStates.COMPLETED;
trade.completedAt = new Date();
trade.isInEscrow = false;
// Subtracting the value of the trade amount from the escrow amount
const sellerProfile = await this.profileRepository.findOne({
where: { id: trade.sellerProfileId },
});
if (!sellerProfile) {
throw new NotFoundException('Seller profile not found!');
}
sellerProfile.escrowBalance -= trade.cryptoAmount;
// Adding the trade amount to the trade escrow amount
trade.tradeEscrowAmount = trade.cryptoAmount;
await this.profileRepository.save(sellerProfile);
const updatedTrade = await this.repository.save(trade);
if (previousState === TradeStates.PAYMENT_ACCEPTED) {
/**
* Update buyer rating and stats
*/
const buyerProfile = await this.profileRepository.findOne({
where: { id: trade.buyerProfileId },
});
buyerProfile.tradeVolume =
parseFloat(buyerProfile.tradeVolume.toString()) +
parseFloat(trade.cryptoAmount);
buyerProfile.totalTrades += 1;
const buyerRating = await this.calculateProfileRating(
trade.buyerProfileId,
);
buyerProfile.rating = buyerRating;
if (trade.tradeType === 'sell') {
buyerProfile.tradeOfferVolume =
parseFloat(buyerProfile.tradeOfferVolume.toString()) +
parseFloat(trade.cryptoAmount);
buyerProfile.totalOffersCompleted += 1;
}
await this.profileRepository.update(trade.buyerProfileId, buyerProfile);
/**
* Update seller rating and stats
*/
sellerProfile.tradeVolume =
parseFloat(sellerProfile.tradeVolume.toString()) +
parseFloat(trade.cryptoAmount);
sellerProfile.totalTrades += 1;
const sellerRating = await this.calculateProfileRating(
trade.sellerProfileId,
);
sellerProfile.rating = sellerRating;
if (trade.tradeType === 'buy') {
sellerProfile.tradeOfferVolume =
parseFloat(sellerProfile.tradeOfferVolume.toString()) +
parseFloat(trade.cryptoAmount);
sellerProfile.totalOffersCompleted += 1;
}
await this.profileRepository.update(trade.sellerProfileId, sellerProfile);
}
return {
message: 'Trade payment has been completed successfully!',
trade: {
state: updatedTrade.state,
completedAt: updatedTrade.completedAt,
id: updatedTrade.id,
},
};
}
Subtracting the Trade Amount from the Escrow Balance:
escrowBalance
.Adding the Trade Amount to the Trade Escrow Amount:
trade.tradeEscrowAmount
with the trade amount.Save the Updated Seller Profile and Trade:
escrowBalance
.tradeEscrowAmount
.These changes ensure that when a trade is completed, the value of the trade amount is subtracted from the escrow balance of the seller and added to the trade escrow amount.
Yes, the two blocks of code you provided should be integrated into your OfferService
class. Here’s how you can include them:
deactivateOffer
MethodYou should add the deactivateOffer
method to the OfferService
class. This method will handle the logic for deactivating an offer and updating the escrow amounts.
calculateTotalAmountFromTrades
MethodYou should also add the calculateTotalAmountFromTrades
method to the OfferService
class. This method will be used by deactivateOffer
to calculate the total amount from trades associated with an offer.
Here’s how you can incorporate both methods into your OfferService
:
@Injectable()
export class OfferService {
private readonly logger = new Logger(OfferService.name);
private cache: NodeCache;
constructor(
@InjectRepository(Offer) private readonly repository: Repository<Offer>,
@InjectRepository(DutyHour) private readonly repositoryDH: Repository<DutyHour>,
@InjectRepository(ExternalRate) private readonly repositoryExternalRate: Repository<ExternalRate>,
@InjectRepository(Profile) private readonly profileRepository: Repository<Profile>, // Add Profile repository
@InjectRepository(Trade) private readonly tradeRepository: Repository<Trade>, // Add Trade repository
private algoliaService: AlgoliaService,
) {
this.cache = new NodeCache({ stdTTL: 100, checkperiod: 120 });
}
// Add this method
async deactivateOffer(id: string): Promise<void> {
const offer = await this.repository.findOne({
where: { id },
relations: ['profile', 'trades'], // Adjust the relations based on your actual entities
});
if (!offer) {
throw new Error('Offer not found');
}
// Calculate the total amount from trades associated with this offer
const totalAmountFromTrades = await this.calculateTotalAmountFromTrades(id);
// Update the profile’s escrow balance
if (offer.profile) {
offer.profile.escrowBalance += totalAmountFromTrades;
await this.profileRepository.save(offer.profile);
}
// Update the offer's escrow amount
offer.escrowAmount -= totalAmountFromTrades;
await this.repository.save(offer);
// Optionally, handle any additional cleanup or notifications
}
// Add this method
private async calculateTotalAmountFromTrades(offerId: string): Promise<number> {
// Adjust this query based on your actual trade model and relations
const trades = await this.tradeRepository.find({
where: { offerId },
});
// Sum up the amounts from the trades
return trades.reduce((total, trade) => total + trade.amount, 0);
}
// Other methods...
}
@InjectRepository(Profile)
to inject the Profile
repository since it's used in the deactivateOffer
method.@InjectRepository(Trade)
to inject the Trade
repository since it's used in the calculateTotalAmountFromTrades
method.deactivateOffer
and calculateTotalAmountFromTrades
methods to the OfferService
class.trades
relation) and adjust if necessary.Feel free to adjust the code based on your specific entity definitions and relations. Let me know if you need further assistance!
To address this requirement, you need to make several modifications to your service logic. Here’s a step-by-step guide on where and how to implement these changes:
When an offer is deactivated, you need to:
escrowBalance
is adjusted accordingly.Here's a general approach:
OfferService
Add a new method to handle the deactivation of an offer. This method should:
escrowBalance
to reflect the released funds.async deactivateOffer(id: string): Promise<void> {
const offer = await this.repository.findOne({
where: { id },
relations: ['profile', 'trades'], // Assuming 'trades' is the relation for trades related to the offer
});
if (!offer) {
throw new Error('Offer not found');
}
// Calculate the total amount from trades associated with this offer
const totalAmountFromTrades = await this.calculateTotalAmountFromTrades(id);
// Update the profile’s escrow balance
if (offer.profile) {
offer.profile.escrowBalance += totalAmountFromTrades;
await this.profileRepository.save(offer.profile);
}
// Update the offer's escrow amount
offer.escrowAmount -= totalAmountFromTrades;
await this.repository.save(offer);
// Optionally, handle any additional cleanup or notifications
}
Add a method to calculate the total amount from trades related to the offer:
private async calculateTotalAmountFromTrades(offerId: string): Promise<number> {
// Adjust this query based on your actual trade model and relations
const trades = await this.tradeRepository.find({
where: { offerId },
});
// Sum up the amounts from the trades
return trades.reduce((total, trade) => total + trade.amount, 0);
}
Ensure your Offer
entity has an escrowAmount
field and that it’s properly updated when an offer is deactivated.
@Entity()
export class Offer {
@PrimaryGeneratedColumn('uuid')
id: string;
// Other fields...
@Column({ type: 'decimal', default: 0 })
escrowAmount: number;
@ManyToOne(() => Profile, profile => profile.offers)
profile: Profile;
@OneToMany(() => Trade, trade => trade.offer)
trades: Trade[];
}
Ensure your Profile
entity has an escrowBalance
field.
@Entity()
export class Profile {
@PrimaryGeneratedColumn('uuid')
id: string;
// Other fields...
@Column({ type: 'decimal', default: 0 })
escrowBalance: number;
@OneToMany(() => Offer, offer => offer.profile)
offers: Offer[];
}
Ensure that the deactivation of an offer triggers the deactivateOffer
method. This could be part of an existing method or a new endpoint in your controller:
@Controller('offers')
export class OfferController {
constructor(private readonly offerService: OfferService) {}
@Patch(':id/deactivate')
async deactivateOffer(@Param('id') id: string) {
await this.offerService.deactivateOffer(id);
return { message: 'Offer deactivated and escrow updated' };
}
}
Test the new functionality thoroughly:
deactivateOffer
and calculateTotalAmountFromTrades
methods.Implement these changes carefully and test thoroughly to ensure that the escrow amount is accurately updated when an offer is deactivated. If you have any questions or need further assistance with specific parts of the implementation, feel free to ask!
[DASHBOARD] Add migration to save the current amount in escrow to the tradeEscrow column in profiles
To add a migration in Rails to save the current amount in escrow to a new column (trade_escrow
) in the profiles
table, you'll need to follow these steps:
Generate a Migration: Create a new migration to add the trade_escrow
column to the profiles
table.
Update the Column with Current Escrow Amount: Modify the migration to update the new column with the current escrow amount.
Run the Migration: Apply the migration to your database.
Here’s a step-by-step guide:
Use the Rails generator to create a new migration:
rails generate migration AddTradeEscrowToProfiles trade_escrow:decimal
This command creates a new migration file that adds a trade_escrow
column of type decimal
to the profiles
table.
Open the newly generated migration file in db/migrate/
. It should look something like this:
class AddTradeEscrowToProfiles < ActiveRecord::Migration[6.0]
def change
add_column :profiles, :trade_escrow, :decimal, precision: 10, scale: 2
end
end
To also update the trade_escrow
column with the current escrow amount, you can add a def up
method to handle this. Here’s an example of how to modify the migration:
class AddTradeEscrowToProfiles < ActiveRecord::Migration[6.0]
def up
add_column :profiles, :trade_escrow, :decimal, precision: 10, scale: 2
# Assuming you have a method to calculate the current escrow amount
# You may need to adjust this based on your application logic
Profile.reset_column_information # Ensure that the new column is loaded
Profile.find_each do |profile|
profile.update_column(:trade_escrow, profile.escrow_amount) # Adjust as needed
end
end
def down
remove_column :profiles, :trade_escrow
end
end
Apply the migration to your database:
rails db:migrate
If you need to update your Profile
model to include any logic for trade_escrow
, add it to app/models/profile.rb
:
class Profile < ApplicationRecord
# Ensure `trade_escrow` is accessible and managed as needed
# Example: validating presence or numericality
validates :trade_escrow, numericality: { greater_than_or_equal_to: 0 }
end
After running the migration, check your profiles
table to ensure the trade_escrow
column has been added and populated correctly.
rails generate migration AddTradeEscrowToProfiles trade_escrow:decimal
trade_escrow
column with current escrow amounts.rails db:migrate
Feel free to adjust the migration logic based on your specific application needs and business rules!
To move the current active offers' amounts to the escrow_amount
column in the profiles
table via a Rails migration, follow these steps:
Generate a Migration: Create a new migration to perform the necessary updates.
Update the Migration: Add logic to move the amounts from active offers to the escrow_amount
column in the profiles
table.
Run the Migration: Apply the migration to your database.
Here’s how you can implement it:
Create a new migration file using the Rails generator:
rails generate migration MoveActiveOffersToEscrow
Open the newly generated migration file in db/migrate/
. It should look like this:
class MoveActiveOffersToEscrow < ActiveRecord::Migration[6.0]
def up
# Update profiles to move amounts from active offers to escrow_amount
# Assuming you have an Offer model and Profile model with appropriate associations
# Retrieve all active offers
active_offers = Offer.where(active: true)
# Group by profile to accumulate the total escrow amount
offer_amounts_by_profile = active_offers.group(:profile_id).sum(:amount)
# Update each profile with the accumulated escrow amount
offer_amounts_by_profile.each do |profile_id, amount|
profile = Profile.find(profile_id)
profile.update_column(:escrow_amount, profile.escrow_amount + amount)
end
end
def down
# Optional: Define how to revert the changes if needed
# For instance, if you need to set escrow_amount to the original values, you would implement that here
end
end
escrow_amount
column.Apply the migration to your database with:
rails db:migrate
Check the profiles
table to ensure that the escrow_amount
column has been updated correctly based on the amounts from active offers.
If you need to provide a way to revert the migration, you could implement logic in the down
method. For example:
def down
# Reset escrow_amount for each profile to its original state
# You would need to have a way to track the original amounts if you want to support rollback
end
rails generate migration MoveActiveOffersToEscrow
escrow_amount
.rails db:migrate
profiles
table.Adjust the migration logic according to your specific schema and business logic.
[P2P-API] Add ecrowAmount and tradeEscrow amount to profiles
RUN
npx typeorm migration:create src/migrations/AddEscrowAmountsToProfiles
npx typeorm migration:create src/migrations/AddTradeEscrowAmountsToProfiles
THEN MODIFY MIGRATION
yarn run typeorm:run-migrations