how to get price range with django? - python

i have price model
class Product(models.Model):
price = models.IntegerField
membership_discount = models.DecimalField
if i get price parameter, (ex. min_price = 100000, max_price = 500000)
I want to get the products multiplied by the price fields and membership_discount fields.
not this
Product.objects.filter(price__range = (min_price, max_price))
i want
Product.objects.filter(price * (1+membership_discount)__range = (min_price, max_price))

lte = less than or equal to
gte = greater than or equal to
this is documentation: https://docs.djangoproject.com/en/4.0/ref/models/querysets/#gt
max_price = #max price logic here
min_price = #min price logic her
#this will filter all products (price <= max_price and price >= min_price)
Product.objects.filter(price__lte = max_price, price__gte = min_price)

You could use annotations for the QuerySet and apply the filter on the annotation.
Product.objects.annotate(
member_price=F('price') * (1 + F('membership_discount'))
).filter(
member_price__range=(min_price, max_price)
)
If the pricefield and the membership_dicsount do not have the same type, you might need to make usage of the ExpressionWrapper with a specific output_field
Product.objects.annotate(
member_price=ExpressionWrapper(
F('price') * (1 + F('membership_discount')),
output_field=DecimalField()
)
).filter(
member_price__range=(min_price, max_price)
)
Docs:
https://docs.djangoproject.com/en/4.0/ref/models/querysets/#django.db.models.query.QuerySet.annotate
https://docs.djangoproject.com/en/4.0/topics/db/aggregation/
https://docs.djangoproject.com/en/4.0/ref/models/expressions/#using-f-with-annotations

Related

Odoo - add "%" next to float field in tree view

How can I add "%" next to value in tree view column for below field:
rec.reserved_qty_per = round(rec.sum_reserved_qty / rec.sum_dmd_qty * 100)
when I used to add (+ "%") it's giving me an error that can't mix between float and str fields.
Here's is my Code:
sum_dmd_qty = fields.Float(compute='calculate_dmd_qty', string='Total Ordered Quantity', digits=(12,0))
sum_reserved_qty = fields.Float(compute='calculate_reserved_qty', string='Total Ready Quantity', digits=(12,0))
reserved_qty_per = fields.Float(compute='_compute_percentage', string='Ready (%)', digits=(12,0))
#api.depends('sum_reserved_qty', 'sum_dmd_qty')
def _compute_percentage(self):
for rec in self:
if rec.sum_dmd_qty:
rec.reserved_qty_per = round(rec.sum_reserved_qty / rec.sum_dmd_qty * 100)
The easiest one is to change reserved_qty_per to a Char field.
reserved_qty_per_chr = fields.Char(compute='_compute_percentage', string='Ready (%)')
#api.depends('sum_reserved_qty', 'sum_dmd_qty')
def _compute_percentage(self):
for rec in self:
if rec.sum_dmd_qty:
qty = round(rec.sum_reserved_qty / rec.sum_dmd_qty * 100)
rec.reserved_qty_per = "{0} {1}".format(qty,"%")

Get values in column between two dates in another columns in Python Django

kindly request function to get and calculates values in between time period with Python-Django function
the database example will be as below
Case(1) start_time = 01-11-2019 end_time = 15-12-2019 rate = 35.00
Case(2) start_time = 16-12-2019 end_time = 31-12-2019 rate = 50.00
i need function to calculate the rate as following:
user will request the period from 13-12-2019 till 18-12-2019
rate calculated [(13, 14, 15 December) = 35+35+35 = 105] + [(16, 17 , 18
December = 50+50+50 = 150] with total rate finally 255
class Items(models.Model):
name = models.CharField(max_length=200, db_index=True)
class Item(models.Model):
name = models.ForeignKey(Items, on_delete=models.CASCADE)
start_date = models.DateField()
end_date = models.DateField()
rate = models.DecimalField(max_digits=3, decimal_places=2)
I think this is what you're looking for. However, I'd be worried about multiple Item instances covering the same range of dates. If that's a real possibility, then the below function won't work properly.
request_lower = some_date
request_upper = some_other_date
items_within_range = Item.objects.filter(
Q(start_date__lte=request_lower, end_date__gt=request_lower)
| Q(start_date__lte=request_upper, end_date__gt=request_upper)
)
total = 0
for index in range((request_upper - request_lower).days):
# Iterate over the days from lower to upper.
date = request_lower + timedelta(days=index)
# Find the Item instance that the date is within the range.
total += [
i.rate for i in items_within_range
if i.start_date <= date < i.end_date
][0]

Django - can I do this without a raw query

I am learning Django, and have gotten quite a long way using the documentation and various other posts on StackOverflow, but I am a bit stuck now. Essentially, I want to query the database as follows:
SELECT
w.wname,
w.act_owner_id,
wi.act_code,
wi.act_provider,
SUM(ft.quantity) AS "position",
prices.Current,
prices.MonthEnd,
prices.YearEnd,
cost.avgcost,
sec.securityName AS "security"
FROM
finance_wrapperinstance as wi
INNER JOIN finance_wrapper as w ON
(w.id = wi.wrapperType_id)
INNER JOIN finance_security as sec ON
(ft.security_id = sec.id)
left outer JOIN finance_transaction as ft ON
(wi.id = ft.investwrapperID_id)
left outer Join
(SELECT
hp.security_id as secid,
max(Case when hp.date = '2019-11-18' then hp.price end) as 'Current',
max(Case when hp.date = '2019-10-30' then hp.price end) as 'MonthEnd',
max(Case when hp.date = '2018-12-31' then hp.price end) as 'yearEnd'
FROM finance_historicprice as hp
GROUP BY hp.security_id
) AS prices ON
(prices.secid =ft.security_id)
INNER JOIN
(SELECT
trans.security_id AS secid,
trans.investwrapperID_id as iwID,
SUM((CASE WHEN trans.buysell = 'b' THEN trans.quantity ELSE 0 END)* trans.price) /
SUM(CASE WHEN trans.buysell = 'b' THEN trans.quantity ELSE 0 END) AS avgCost
FROM
finance_transaction as trans
GROUP BY
trans.security_id,
trans.investwrapperID_id) AS cost ON
(cost.secid = ft.security_id and cost.iwID = wi.id)
GROUP BY
w.wname,
wi.wrapperType_id,
wi.act_code,
wi.act_provider,
ft.security_id
but I don't know how to use the Django ORM to get my prices subquery or cost subquery.
The models look like this:
class Wrapper(models.Model):
wname = models.CharField(max_length=50,null=False,verbose_name="Wrapper Name")
act_owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class Wrapperinstance(models.Model):
wrapperType = models.ForeignKey(Wrapper,on_delete=models.CASCADE)
act_code = models.CharField(max_length=50,null=False, verbose_name="Account Code")
act_provider = models.CharField(max_length=50,null=False,verbose_name="Account Provider")
class Security(models.Model):
securityName = models.CharField(max_length=200,null=False,verbose_name="Security Name")
securityType = models.ForeignKey(InstrumentType,on_delete=models.CASCADE)
class Transaction(models.Model):
BUY = 'b'
SELL = 's'
BUY_SELL_CHOICES = [
(BUY, 'Buy'),
(SELL, 'Sell'),
]
security = models.ForeignKey(Security,default=1,on_delete=models.CASCADE)
investwrapperID = models.ForeignKey(Wrapperinstance,default=1,on_delete=models.CASCADE)
quantity = models.DecimalField(max_digits=14, decimal_places=4)
buysell = models.CharField(max_length=2,choices=BUY_SELL_CHOICES, default = BUY)
price = models.DecimalField(max_digits=14, decimal_places=2)
class HistoricPrice(models.Model):
security = models.ForeignKey(Security,default=1,on_delete=models.CASCADE)
date = models.DateField()
price = models.DecimalField(max_digits=14, decimal_places=2)
Any help or pointers would be greatly appreciated. As an additional point, I have functions which will choose the correct dates to be entered for the SQL query. This again makes me think the RAW method may be the way to go.

Odoo 8 Allow Negative Invoice Line Item

I have a product called "Coupon" with negative amount which is used to offset the product price. However, it seems like Odoo 8 does not allow computation of negative amount to price_subtotal (it becomes 0.00):
Coupon ... ... 1 Each -40.0000 0.0000
When I remove the negative sign, it computes
Coupon ... ... 1 Each 40.0000 40.0000
From an accounting perspective, the total invoice should not be negative. That stays true. However, I do need to allow negative computation of invoice line item(s). Where and what do I need to change? I tried looking into account/account.py but to no avail so far - it's all just "tax" related.
Thanks in advance!
Details of the amount column for the line total
class account_invoice(models.Model)
....
#api.one
#api.depends('invoice_line.price_subtotal', 'tax_line.amount')
def _compute_amount(self):
self.amount_untaxed = sum(line.price_subtotal for line in self.invoice_line)
self.amount_tax = sum(line.amount for line in self.tax_line)
self.amount_total = self.amount_untaxed + self.amount_tax
....
class account_invoice_line(models.Model):
_name = "account.invoice.line"
_description = "Invoice Line"
_order = "invoice_id,sequence,id"
#api.one
#api.depends('price_unit', 'discount', 'invoice_line_tax_id', 'quantity',
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id')
def _compute_price(self):
price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
taxes = self.invoice_line_tax_id.compute_all(price, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id)
self.price_subtotal = taxes['total']
if self.invoice_id:
self.price_subtotal = self.invoice_id.currency_id.round(self.price_subtotal)
#api.model
def _default_price_unit(self):
if not self._context.get('check_total'):
return 0
total = self._context['check_total']
for l in self._context.get('invoice_line', []):
if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
vals = l[2]
price = vals.get('price_unit', 0) * (1 - vals.get('discount', 0) / 100.0)
total = total - (price * vals.get('quantity'))
taxes = vals.get('invoice_line_tax_id')
if taxes and len(taxes[0]) >= 3 and taxes[0][2]:
taxes = self.env['account.tax'].browse(taxes[0][2])
tax_res = taxes.compute_all(price, vals.get('quantity'),
product=vals.get('product_id'), partner=self._context.get('partner_id'))
for tax in tax_res['taxes']:
total = total - tax['amount']
return total
Odoo's default behaviour is handling it as expected. The problem is custom code. (For more information read the questions comments)

Django filter on annotation from custom aggregate function

I'm trying to find all zipcodes within a certain distance of a given zipcode. I'm doing this by calculating distance using a custom-made aggregate function, annotating the queryset with the distance, and filtering the queryset based on that 'distance' field.
The aggregate function calculates the distance correctly, and the annotation creates the 'distance' field in the queryset correctly. However, the filter always returns an empty queryset. It works fine when I filter using other fields such as 'zipcode' or 'state', but returns empty when I use the annotated 'distance' value as the filter value. What am I doing wrong?
Here is the custom aggregate function:
from django.db import models
from django.db.models import Aggregate
from django.db.models.sql.aggregates import Aggregate as AggregateImpl
class DistanceFromImpl(AggregateImpl):
sql_function = ''
is_computed = True
is_ordinal = True
sql_template = ('3959 * acos( cos( radians(%(t_lat)f) ) * cos( radians( latitude ) ) * '
'cos( radians( longitude ) - radians(%(t_lon)f) ) + sin( radians(%(t_lat)f) ) * '
'sin( radians( latitude ) ) )')
def __init__(self, col, target, **extra):
self.col = col
self.target = target
self.extra = extra
def _default_alias(self):
return '%s__%s' % (str(self.target), self.__class__.__name__.lower())
default_alias = property(_default_alias)
def add_to_query(self, query, alias, col, source, is_summary):
super(DistanceFrom, self).__init__(col, source, is_summary, **self.extra)
query.aggregate_select[alias] = self
def as_sql(self, qn, connection):
"Return the aggregate, rendered as SQL."
return self.sql_template % { 't_lon': self.target.longitude,
't_lat': self.target.latitude }
class DistanceFrom(Aggregate):
name="DistanceFromImpl"
def add_to_query(self, query, alias, col, source, is_summary):
aggregate = DistanceFromImpl(col, source=source, is_summary=is_summary, **self.extra)
query.aggregates[alias] = aggregate
I grabbed this and the other code from here:
https://github.com/elfsternberg/django-zipdistance/blob/master/zipdistance/models.py
My model for a zipcode is called ZipDistance. I can easily get a queryset with annotated distances from a given ZipDistance. So this works fine:
>>> zip1 = ZipDistance.objects.get(zipcode='01234')
>>> qs = ZipDistance.objects.annotate(distance=DistanceFrom('zipcode', target=zip1))
>>> qs[1].distance
5 # Second entry in queryset is 5 miles away from zipcode '01234'
But any filtering by distance always returns empty:
>>> qs.filter(distance__lte=99999)
[]
I'm using my own fixture to populate my database (which is MySQL). The problem could be that I'm using Django version 1.5, and the code was written for an earlier version. I'm just not sure, I've been trying everything I can think of for a few days now.
Still not sure what was causing this error, but I got around it by writing my own SQL code in a method:
from django.db import models, connection, transaction
from math import sin, cos, radians, acos
class ZipDistance(models.Model):
zipcode = models.CharField(max_length=5, unique=True)
state_short = models.CharField(max_length=2)
latitude = models.FloatField()
longitude = models.FloatField()
province = models.CharField(max_length=50)
state_long = models.CharField(max_length=20)
def get_zips_within(self, dist):
cursor = connection.cursor()
cursor.execute("""SELECT id, (
3959 * acos( cos( radians(%s) ) * cos( radians( latitude ) ) *
cos( radians( longitude ) - radians(%s) ) + sin( radians(%s) ) *
sin( radians( latitude ) ) ) )
AS distance FROM myapp_zipdistance
HAVING distance <= %s""",
[self.latitude, self.longitude, self.latitude, dist])
ids = [row[0] for row in cursor.fetchall()]
return ZipDistance.objects.filter(id__in=ids)
Now, all I have to do to get a list of zipcodes within a certain distance is the following:
>>> zip1 = ZipDistance.objects.get(zipcode='20001')
>>> zip_list = zip1.get_zips_within(dist=100)
This gives me a list of all the zipcodes in my database within 100 miles of Washington D.C. (zipcode 20001)

Categories