How to deduct funds from users withdrawals in Django Rest Framework - django-rest-framework

I am building a logistics web application using DRF and i want to set users to withdrawal certain amount, let's say a users withdraw $200, i want the users to get $180 this means minus $20 from each withdrawals, but when i tried using the API, i am getting $220 instead of $180, which means my code is returning a plus sign for withdrawals instead of negative sign.
Below is the codes from my Models
class UserWallet(models.Model):
wallet_id = models.UUIDField(unique=True, default=uuid.uuid4)
user = models.OneToOneField("accounts.User", on_delete=models.CASCADE, related_name="wallet")
currency = models.CharField(max_length=10, default="NGN")
created_at = models.DateTimeField(default=timezone.now)
def get_balance(self):
query = (Q(status="success") | Q(status="processing")) & Q(wallet=self)
balance = WalletTransaction.objects.filter(
query).aggregate(Sum('amount'))
return balance
def to_dict(self):
balance = self.get_balance()["amount__sum"]
return {
"wallet_id": self.wallet_id,
"balance": f"{balance:.2f}" if balance else "0.00",
"currency": self.currency,
"currency_symbol": "₦"
}
def get_earnings(self):
total = WalletTransaction.objects.filter(
wallet=self, status="success", transaction_type="payment").aggregate(Sum('amount'))
return total
def get_withdrawals(self):
total = WalletTransaction.objects.filter(
wallet=self, status="success", transaction_type="withdrawal").aggregate(Sum('amount'))
return total
def get_transfers(self):
total = WalletTransaction.objects.filter(
wallet=self, status="success", transaction_type="transfer").aggregate(Sum('amount'))
return total
def get_deposits(self):
total = WalletTransaction.objects.filter(
wallet=self, status="success", transaction_type="deposit").aggregate(Sum('amount'))
return total
Codes from the Views
#api_view(["POST"])
#transaction.atomic
def initialize_transaction(request):
payload = request.data
user = request.user
wallet = UserWallet.objects.get(user=user)
if payload["transaction_type"] == "deposit":
ser = DepositSerializer(data=request.data)
if not ser.is_valid():
return ApiResponse(message=error_to_string(ser.errors), data=ser.errors, status_code=400).response()
transaction = WalletTransaction.objects.create(
wallet=wallet,
amount=payload["amount"],
description="WALLET TOP UP",
transaction_type="deposit"
)
return Response({
"status": True,
"data": {
"email": user.email,
"amount": payload["amount"],
"reference": transaction.reference
}
})
if payload['transaction_type'] == "transfer":
ser = TransferSerializer(data=request.data, context={"request": request})
if not ser.is_valid():
return ApiResponse(message=error_to_string(ser.errors), data=ser.errors, status_code=400).response()
transaction = WalletTransaction.objects.create(
wallet=wallet,
amount=ser.validated_data["amount"]*-1,
description=payload['description'],
transaction_type="transfer",
extras=payload
)
otp = OTP.generate_otp(wallet)
task.otp_mail(request.user.email, {"code": otp})
data = {
"transaction_id": transaction.transaction_id,
}
return ApiResponse(data=data, message="otp sent").response()
if payload['transaction_type'] == "withdrawal":
ser = WithdrawSerializer(data=request.data, context={"request": request})
if not ser.is_valid():
return ApiResponse(message=error_to_string(ser.errors), data=ser.errors, status_code=400).response()
payload['bank'] = ser.validated_data['bank'].json()
transaction = WalletTransaction.objects.create(
wallet=wallet,
amount=ser.validated_data["amount"]*-1,
description=payload['description'],
transaction_type="withdrawal",
extras=payload
)
otp = OTP.generate_otp(wallet)
task.otp_mail(request.user.email, {"code": otp})
data = {
"transaction_id": transaction.transaction_id,
}
return ApiResponse(data=data, message="otp sent").response()
return ApiResponse(status_code=400, message="Invalid TransactionType").response()

Related

MockK no answer found for:

I'm trying to test a method in my Service Implementation with JUnit and MockK library.
PlanServiceFeatureImplTest.kt
#Test
fun `storeInstallmentPlan`(#MockK user: User) {
val debtId = 123L
val amount = BigDecimal.valueOf(1000)
val interval = Repeat.monthly
val firstPaymentDate = LocalDate.of(
2021, 11, 4
).atStartOfDay(Time.DEFAULT_TIME_ZONE).toOffsetDateTime()
val planDTO = InstallmentPlanDTO(
interval = interval,
firstPaymentDate = firstPaymentDate,
amount = amount,
installments = listOf()
)
val debt = Debt(
userId = 32L,
title = "debt1",
amount = amount,
category = DebtCategory.credit
)
val plan = InstallmentPlan(
id = 122L,
debtId = debtId,
interval = interval,
firstPaymentDate = firstPaymentDate,
amount = amount
)
val installment1 = Installment(
id = 34,
debtId = debtId,
recordId = 13
)
val installment2 = Installment(
id = 35,
debtId = debtId,
recordId = 14
)
val newStartDate = ZonedDateTime.parse("2021-10-05T00:00+02:00[Europe/Berlin]")
val newEndDate = ZonedDateTime.parse("2021-11-04T00:00+01:00[Europe/Berlin]")
val installments = listOf(
WalletRecord(
userId = debt.userId,
type = RecordType.debt_rate,
amount = plan.amount,
title = debt.title,
category = RecordCategory.debt_rate,
repeat = plan.interval,
startDate = newStartDate.toOffsetDateTime(),
endDate = newEndDate.toOffsetDateTime(),
)
)
val records = flowOf(
WalletRecord(
id = 43,
userId = debt.userId,
type = RecordType.debt_rate,
amount = plan.amount,
title = debt.title,
category = RecordCategory.debt_rate,
repeat = plan.interval,
startDate = newStartDate.toOffsetDateTime(),
endDate = newEndDate.toOffsetDateTime(),
)
)
every { user.tz } returns "Europe/Berlin"
coEvery { debtRepository.findById(debtId) } returns debt
coEvery { installmentPlanRepository.findByDebtId(debtId) } returns plan
coEvery {
installmentPlanRepository.save(
planDTO.copy(
amount = amount
).toEntity(
id = plan.id,
debtId = debtId
)
)
} returns InstallmentPlan(plan.id, debtId, interval, firstPaymentDate, amount )
coEvery { installmentRepository.findByDebtId(debtId) } returns flowOf(installment1, installment2)
coEvery {
installmentRepository.deleteAllById(listOf(installment1.id!!, installment2.id!!).asIterable())
} just Runs
coEvery { recordService.deleteAll(listOf(installment1.recordId, installment2.recordId)) } just Runs
coEvery { userService.findById(debt.userId) } returns user
coEvery { recordService.saveAll(installments) } returns records
coEvery {
installmentRepository.saveAll(
records.map {
Installment(
debtId = debtId,
recordId = it.id!!
)
}
).map { it.recordId }
} returns flowOf(43)
runBlocking { planService.storeInstallmentPlan(debtId, planDTO) }
}
PlanServiceFeatureImpl.kt
#Transactional
override suspend fun storeInstallmentPlan(debtId: Long, installmentPlanDTO: InstallmentPlanDTO): Flow<Long> {
val debt = debtRepository.findById(debtId) ?: throw NotFoundException("Could not find debt with id $debtId")
val installmentPlanId = installmentPlanRepository.findByDebtId(debtId)?.id
val minimumAmount =
BigDecimal.ONE.max(debt.amount.multiply(MINIMUM_INSTALLMENT_PERCENTAGE_OF_TOTAL_AMOUNT, MathContext.DECIMAL32))
.setScale(2, RoundingMode.HALF_UP)
.stripTrailingZeros()
val maximumAmount = debt.amount
val clampedAmount = maximumAmount.min(minimumAmount.max(installmentPlanDTO.amount))
val installmentPlan = installmentPlanDTO.copy(
amount = clampedAmount,
).toEntity(
id = installmentPlanId,
debtId = debtId,
)
installmentPlanRepository.save(installmentPlan)
// delete existing records associated with the installment plan
val existingInstallments = installmentRepository.findByDebtId(debtId).toList()
installmentRepository.deleteAllById(existingInstallments.map { it.id!! })
recordService.deleteAll(existingInstallments.map { it.recordId })
// calculate installments / records
/*
This calculation follows this invariant:
debt.amount = countOfFullInstallments * amount + lastInstallment
*/
val user = userService.findById(debt.userId)
?: throw NotFoundException("Could not find user that owns debt $debtId")
val zoneId = ZoneId.of(user.tz)
val firstPaymentDate = installmentPlan.firstPaymentDate.atZoneSameInstant(zoneId)
val countOfFullInstallments =
debt.amount.divide(
(if (installmentPlan.amount <= BigDecimal.ONE) BigDecimal.ONE else installmentPlan.amount),
MathContext.DECIMAL32
)
.setScale(0, RoundingMode.DOWN)
.intValueExact()
val lastInstallmentAmount = debt.amount - installmentPlan.amount * countOfFullInstallments.toBigDecimal()
val countOfInstallments = countOfFullInstallments + if (lastInstallmentAmount > BigDecimal.ZERO) 1 else 0
val installments = List(countOfInstallments) { i ->
val endDate =
addInterval(firstPaymentDate, installmentPlan.interval, i)
val startDate = addInterval(firstPaymentDate, installmentPlan.interval, i - 1)
.plusDays(1)
val recordAmount = if (i < countOfFullInstallments)
installmentPlan.amount
else lastInstallmentAmount
WalletRecord(
userId = debt.userId,
type = RecordType.debt_rate,
amount = recordAmount,
title = debt.title,
category = RecordCategory.debt_rate,
repeat = installmentPlan.interval,
startDate = startDate.toOffsetDateTime(),
endDate = endDate.toOffsetDateTime(),
)
}
val recordsFlow = recordService.saveAll(installments)
return installmentRepository.saveAll(recordsFlow.map {
Installment(
debtId = debtId,
recordId = it.id!!,
)
}).map { it.recordId }
}
I get this error:
no answer found for: InstallmentRepository(installmentRepository#4).saveAll(app.backend.plan.PlanServiceFeatureImpl$storeInstallmentPlan$suspendImpl$$inlined$map$1#31e1ec3)
io.mockk.MockKException: no answer found for: InstallmentRepository(installmentRepository#4).saveAll(app.backend.plan.PlanServiceFeatureImpl$storeInstallmentPlan$suspendImpl$$inlined$map$1#31e1ec3)
It is a lot of code, but since I don't know where the error comes from I provide the full code. In other cases with the error 'no answer found for' it provided me something like '[...]InstallmentRepository(...).saveAll([parameters here])' and not a path to the Unit under test.
Hope someone can help me with this.
You are trying to mock the following call:
installmentRepository.saveAll(recordsFlow.map {
Installment(
debtId = debtId,
recordId = it.id!!,
)
}).map { it.recordId }
But what you really need to mock is only saveAll(), not the map() after it as follows:
coEvery { installmentRepository.saveAll(
records.map {
Installment(
debtId = debtId,
recordId = it.id!!
)
}
)
} returns flowOf(Installment(debtId, 43))
If this does not work, try the following (with a less strict matching):
coEvery { installmentRepository.saveAll(ArgumentMatchers.any()) } returns flowOf(Installment(debtId, 43))

Missing customer name/address information in SquareUp Console using Square.Connect.Api

We are creating a Square 2.0 transaction but when we login to our Square account, the customer name/address information is missing. We are creating a customer address object and populating the properties but they are not showing up in the Square console.
Here is our code:
using Square.Connect.Api;
using Square.Connect.Model;
using Square.Connect.Client;
private static bool RunCreditCardSquare(int? adminId, Models.ReservationReceivePaymentViewModel payment, Models.GuestViewModel guest, String ipaddress, String orderdescription, int reservationId, decimal amountDue)
{
Data.BigRigEntities db = new Data.BigRigEntities();
TransactionsApi transactionsApi = new TransactionsApi();
Square.Connect.Client.Configuration.Default.AccessToken = accessToken;
string uuid = Guid.NewGuid().ToString();
Money amount = new Money((long)amountDue * 100, Money.CurrencyEnum.USD);
// (https://docs.connect.squareup.com/payments/transactions/overview#mpt-overview).
ChargeRequest body = new ChargeRequest(AmountMoney: amount, IdempotencyKey: uuid, CardNonce: payment.CardNonce);
Square.Connect.Model.Address.CountryEnum GuestCountry = Square.Connect.Model.Address.CountryEnum.US;
body.ShippingAddress = new Square.Connect.Model.Address(guest.Address1, "", "", guest.City, "", "", "", "", "", "", guest.PostalCode, GuestCountry, guest.FirstName, guest.LastName, "");
body.BillingAddress = new Square.Connect.Model.Address(guest.Address1, "", "", guest.City, "", "", "", "", "", "", guest.PostalCode, GuestCountry, guest.FirstName, guest.LastName, "");
body.ReferenceId = reservationId.ToString() ;
body.CardNonce = payment.CardNonce;
body.BuyerEmailAddress = guest.EmailAddress;
body.Note = "Conf #:" + reservationId.ToString() + " - " + orderdescription;
var charged = false;
var transactionid = "";
var error = "";
decimal chargeamount = 0.0M;
try
{
var response = transactionsApi.Charge(LocationId, body);
transactionid = response.Transaction.Id;
chargeamount = (decimal) (response.Transaction.Tenders[0].AmountMoney.Amount/100) ;
charged = true;
}
catch (ApiException e)
{
error = e.Message;
}
The shipping address and billing address are only available via API, not the Square Dashboard. To see this information attached to a customer in the Dashboard you should Create a Customer first and then pass the customer_id along when you charge.

django rest framework:In Serializer how to show the field properties also

I have model:
class Ingredient(models.Model):
KILOGRAM = 'kg'
LITER = 'ltr'
PIECES = 'pcs'
MUNITS_CHOICES = (
(KILOGRAM, 'Kilogram'),
(LITER, 'Liter'),
(PIECES, 'Pieces'),
)
name = models.CharField(max_length=200,unique=True,null=False)
slug = models.SlugField(unique=True)
munit = models.CharField(max_length=10,choices=MUNITS_CHOICES,default=KILOGRAM)
rate = models.DecimalField(max_digits=19, decimal_places=2,validators=[MinValueValidator(0)],default=0)
typeofingredient = models.ForeignKey(TypeOfIngredient, related_name='typeof_ingredient',null=True, blank=True,on_delete=models.PROTECT)
density_kg_per_lt = models.DecimalField(max_digits=19, decimal_places=2,verbose_name='Density (kg/lt)',null=True,blank=True,validators=[MinValueValidator(0)])
density_pcs_per_kg = models.DecimalField(max_digits=19, decimal_places=2,verbose_name='Density (pcs/kg)',null=True,blank=True,validators=[MinValueValidator(0)])
density_pcs_per_lt = models.DecimalField(max_digits=19, decimal_places=2,verbose_name='Density (pcs/lt)',null=True,blank=True,validators=[MinValueValidator(0)])
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
When i get the api i also want to get field types like char, decimal, datetime etc
Something like the below api result, is it possible. Because i am using reactJs as frontend, i have tell the input what kind of field it can accept and also helps in sorting by text or number
{
"id": {value: 1,type: number},
"name": {value: "adark",type: charfield},
"rate": {value: "12.00",type: decimal},
"updated": {value: "2017-07-14T10:51:47.847171Z",type: datetime},
.......so on
}
The Corresponding Serializer would be as follows:
class IngredientSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
rate = serializers.SerializerMethodField()
updated = serializers.SerializerMethodField()
class Meta:
model = Ingredient
fields = ('name', 'rate', 'updated')
def get_name(self, obj):
response = dict()
response['value'] = obj.name
response['type'] = obj.name.get_internal_type()
return Response(response)
def get_rate(self, obj):
response = dict()
response['value'] = obj.rate
response['type'] = obj.rate.get_internal_type()
return Response(response)
def get_updated(self, obj):
response = dict()
response['value'] = obj.updated
response['type'] = obj.updated.get_internal_type()
return Response(response)

Entity Framework cycle of data

I have an Account object, which has many Transactions related to it.
In one method, I get all transactions for a particular account.
var transactionlines = (from p in Context.account_transaction
.Include("account_transaction_line")
// .Include("Account")
.Include("account.z_account_type")
.Include("account.institution")
.Include("third_party")
.Include("third_party.z_third_party_type")
.Include("z_account_transaction_type")
.Include("account_transaction_line.transaction_sub_category")
.Include("account_transaction_line.transaction_sub_category.transaction_category")
.Include("z_account_transaction_entry_type")
.Include("account_transaction_line.cost_centre")
where p.account_id == accountId
&& p.deleted == null
select p).ToList();
This is meant to return me a list of transactions, with their related objects. I then pass each object to a Translator, which translates them into data transfer objects, which are then passed back to my main application.
public TransactionDto TranslateTransaction(account_transaction source)
{
LogUserActivity("in TranslateTransaction");
var result = new TransactionDto
{
Id = source.id,
Version = source.version,
AccountId = source.account_id,
// Account = TranslateAccount(source.account, false),
ThirdPartyId = source.third_party_id,
ThirdParty = TranslateThirdParty(source.third_party),
Amount = source.transaction_amount,
EntryTypeId = source.account_transaction_entry_type_id,
EntryType = new ReferenceItemDto
{
Id = source.account_transaction_entry_type_id,
Description = source.z_account_transaction_entry_type.description,
Deleted = source.z_account_transaction_entry_type.deleted != null
},
Notes = source.notes,
TransactionDate = source.transaction_date,
TransactionTypeId = source.account_transaction_type_id,
TransactionType = new ReferenceItemDto
{
Id = source.z_account_transaction_type.id,
Description = source.z_account_transaction_type.description,
Deleted = source.z_account_transaction_type.deleted != null
}
};
... return my object
}
The problem is:
An account has Transactions, and a Transaction therefore belongs to an Account. It seems my translators are being called way too much, and reloading a lot of data because of this.
When I load my transaction object, it's 'account' property has a'transactions' propery, which has a list of all the transactions associated to that account. Each transaction then has an account property... and those account peroprties again, have a list of all the transactions... and on and on it goes.
Is there a way I can limit the loading to one level or something?
I have this set:
Context.Configuration.LazyLoadingEnabled = false;
I was hoping my 'Includes' would be all that is loaded... Don't load 'un-included' related data?
As requested, here is my TranslateAccount method:
public AccountDto TranslateAccount(account p, bool includeCardsInterestRateDataAndBalance)
{
LogUserActivity("in TranslateAccount");
if (p == null)
return null;
var result =
new AccountDto
{
Id = p.id,
Description = p.description,
PortfolioId = p.institution.account_portfolio_id,
AccountNumber = p.account_number,
Institution = TranslateInstitution(p.institution),
AccountType = new ReferenceItemDto
{
Id = p.account_type_id,
Description = p.z_account_type.description
},
AccountTypeId = p.account_type_id,
InstitutionId = p.institution_id,
MinimumBalance = p.min_balance,
OpeningBalance = p.opening_balance,
OpeningDate = p.opening_date
};
if (includeCardsInterestRateDataAndBalance)
{
// Add the assigned cards collection
foreach (var card in p.account_card)
{
result.Cards.Add(new AccountCardDto
{
Id = card.id,
AccountId = card.account_id,
Active = card.active,
CardHolderName = card.card_holder_name,
CardNumber = card.card_number,
ExpiryDate = card.expiry
});
}
// Populate the current interest rate
result.CurrentRate = GetCurrentInterestRate(result.Id);
// Add all rates to the account
foreach (var rate in p.account_rate)
{
result.Rates.Add(
new AccountRateDto
{
Id = rate.id,
Description = rate.description,
Deleted = rate.deleted != null,
AccountId = rate.account_id,
EndDate = rate.end_date,
Rate = rate.rate,
StartDate = rate.start_date
});
}
result.CurrentBalance = CurrentBalance(result.Id);
}
LogUserActivity("out TranslateAccount");
return result;
}
The entity framework context maintains a cache of data that has been pulled out of the database. Regardless of lazy loading being enabled/disabled, you can call Transaction.Account.Transactions[0].Account.Transactions[0]... as much as you want without loading anything else from the database.
The problem is not in the cyclical nature of entity framework objects - it is somewhere in the logic of your translation objects.

adding and saving new items to the collection

Assume we have an Customer object with collection of Payments.
Initialization:
var dataContext = new TestDataContext();
dataContext.Customers.InsertOnSubmit(new Customer { Id = 1, Name = "Customer1" });
dataContext.SubmitChanges();
var customer = dataContext.Customers.Where(c => c.Id == 1).First();
First case:
customer.Payments.Add(new Payment { Amount = 100, CustomerId = customer.Id });
dataContext.SubmitChanges();
var count = dataContext.Payments.Count(); // count == 0
Second case:
dataContext.Payments.InsertOnSubmit(new Payment { Amount = 100, Customer = customer });
dataContext.SubmitChanges();
var count = dataContext.Payments.Count(); // count == 1
Third case (combined):
customer.Payments.Add(new Payment { Amount = 100, CustomerId = customer.Id });
dataContext.Payments.InsertOnSubmit(new Payment { Amount = 100, Customer = customer });
dataContext.SubmitChanges();
var count = dataContext.Payments.Count(); // count == 2 (!)
I assume that InsertOnSubmit somehow notifies the DataContext object about changes. But just wondered why it's not notified in the first case?
P.S. I am using SQL CE for Windows Phone.
Have you tried setting the customer reference to Payment.Customer property in the first case?
E.g. customer.Payments.Add(new Payment { Amount = 100, Customer = customer });
I normally wouldn't set IDs explicitly, but make use of the ORM and establish proper relationships between entities.

Resources