Suppose, a measure group doesn't have any relation with Product dimension. We can easily make the report understandable by setting IgnoreUnrelatedDimensions = FALSE. But Total line is still showing the sum value. Is there any way so that i can set null for Total line too? I don't want to set the IsAggregatable = False of Product dimension as I need it for other measure groups. Any help?
Note: I want to have this effect in TOTAL line of any attribute of that dimension. Is there any generic way to do this?
Thanks in advance.
You could put the following into your calculation script:
SCOPE( { Root([Products]) } * MeasureGroupMeasures('name of your measure group'));
This = NULL;
END SCOPE;
And if you want to have the same behavior for two more unrelated dimensions, say DimA and DimB, you would duplicate this block for each dimension like this:
SCOPE( { Root([DimA]) } * MeasureGroupMeasures('name of your measure group'));
This = NULL;
END SCOPE;
SCOPE( { Root([DimB]) } * MeasureGroupMeasures('name of your measure group'));
This = NULL;
END SCOPE;
You must keep these separate, as you want to have the value null for the All elements of DimA, irrespective if the DimB members are at the All level as well or not.
Related
for example I want to update the price of products with a price of zero dollars to 100.
for price_list_items in (self.env['product.pricelist.item'].search_read([], [])):
if price_list_items.price == 0:
price_list_items.price = 100
But It's not working.
AttributeError: 'dict' object has no attribute 'price'
How can I make such a update like this ?
you can do it this way :
list_of_pricelist = self.env['product.pricelist.item'].search([])
for pricelist in list_of_pricelist:
if price_list_items.price == 0:
price_list_items.price = 100
I think you better use search() in your case.
Difference between search() and search_read() :
https://www.odoo.com/fr_FR/forum/aide-1/difference-between-search-and-search-read-in-odoo-8-83076
Search_read() returns a list of dict.
If you really want to do your code this way, i think you can do it like this, but it's not usual
for price_list_items in (self.env['product.pricelist.item'].search_read([], [])):
if price_list_items[<list_index:int>]['field_name'] == 0:
price_list_items[<list_index:int>].update({'field_name': value})
The search_read function returns a list of dicts, price_list_items is a dict of one record field values
Setting the price using the following syntax is available for recordsets:
price_list_recod.price = 100
Instead of fetching and reading all records, you can simply search for price list items with a price equal to 0.0 using the search method (which will return a recordset) and then update the price for all of them with one assignment
Example:
self.env['product.pricelist.item'].search([('price', '=', 0)]).price = 100
In the below code, I am trying to calculate the total price of a basket, where basket is a HashMap containing the products as key and the quantity as value. Promotions are available as a list of Promotion.
I am looping over every map entry and for each of them iterating the promotions. If the promotion matches, I am taking the promotion price (promotion.computeDiscountedPrice()) and removing the promotion from the list (Because a promotion is applicable only to a product & product is unique in the list)
If there is no promotion, we execute block.
if (!offerApplied) { /* .... */ }
Can you please help me in doing this same operation using JAVA 8 stream api?
BigDecimal basketPrice = new BigDecimal("0.0");
Map<String, Integer> basket = buildBasket(input);
List<Promotion> promotions = getOffersApplicable(basket);
for (Map.Entry<String, Integer> entry : trolley.entrySet()) {
boolean offerApplied = false;
Iterator<Promotion> promotionIterator = promotions.iterator();
while (promotionIterator.hasNext()) {
Promotion promotion = promotionIterator.next();
if (entry.getKey().equalsIgnoreCase(offer.getProduct().getProductName())) {
basketPrice = basketPrice.add(promotion.computeDiscountedPrice());
offerApplied = true;
promotionIterator.remove();
break;
}
if (!offerApplied) {
basketPrice = basketPrice.add(Product.valueOf(entry.getKey()).getPrice()
.multiply(new BigDecimal(entry.getValue())));
}
}
return basketPrice;
The simplest and cleaner solution, with a better performance than having to iterate the entire promotions list, is to start by creating a map of promotions identified by the product id (in lower case or upper case [assuming no case collision occurs by the use of equalsIgnoreCase(..)]).
Map<String, Promotion> promotionByProduct = promotions.stream()
.collect(Collectors.toMap(prom -> prom.getProduct()
.getProductName().toLowerCase(), Function.identity()));
This will avoid the need to iterate over the entire array when searching for promotions, it also avoids deleting items from it, which in case of being an ArrayList would need to shift to left the remaining elements each time the remove is used.
BigDecimal basketPrice = basket.keySet().stream()
.map(name -> Optional.ofNullable(promotionByProduct.get(name.toLowerCase()))
.map(Promotion::computeDiscountedPrice) // promotion exists
.orElseGet(() -> Product.valueOf(name).getPrice()) // no promotion
.multiply(BigDecimal.valueOf(basket.get(name))))
.reduce(BigDecimal.ZERO, BigDecimal::add);
It iterates for each product name in the basket, then checks if a promotion exists, it uses the computeDiscountedPrice method, otherwise it looks the product with Product.valueOf(..) and gets the price, after that it mutiplies this value by the quantity of products in the basket and finally the results are reduced (all values of the basket are added) with the BigDecimal.add() method.
Important thing to note, is that in your code, you don't multiply by the quantity the result of promotion.computeDiscountedPrice() (this code above does), i'm not sure if that is a type in your code, or that's the way it should behave.
If case it is in fact the way it should behave (you don't want to multiply quantity by promotion.computeDiscountedPrice()) the code would be:
BigDecimal basketPrice = basket.keySet().stream()
.map(name -> Optional.ofNullable(promotionByProduct.get(name.toLowerCase()))
.map(Promotion::computeDiscountedPrice)
.orElseGet(() -> Product.valueOf(name).getPrice()
.multiply(BigDecimal.valueOf(basket.get(name)))))
.reduce(BigDecimal.ZERO, BigDecimal::add);
Here the only value multiplied by quantity would be the product price obtained with Product.valueOf(name).getPrice().
Finally another option, all in one line and not using the map (iterating over the promotions) using the first approach (multipling by quantity in the end):
BigDecimal basketPrice = basket.keySet().stream()
.map(name -> promotions.stream()
.filter(prom -> name.equalsIgnoreCase(prom.getProduct().getProductName()))
.findFirst().map(Promotion::computeDiscountedPrice) // promotion exists
.orElseGet(() -> Product.valueOf(name).getPrice()) // no promotion
.multiply(BigDecimal.valueOf(basket.get(name))))
.reduce(BigDecimal.ZERO, BigDecimal::add);
I have two models a parent and the son contains two float fields
one of the values this calculates according to the other but when I change the father how can be my calculating function.
Here is my example:
class A(models.model):
trv_ids = fields.One2many(classB,id_A)
class B(models.model):
id_A = fields.Many2one(classA)
qtite = fields.float(default=0)
qtite1 = fields.float(default=0,compute=?????)
qtite1 gets the value of qtite when I change parent
as the example of cumulated amount becomes previous quantity the next month.
Thanks
If i understood right i think what you need is something like this:
#api.depends('id_A')
def _compute_qtie1(self):
for record in self:
record.qtite1 = record.qtite
qtite1 = fields.float(compute=_compute_qtie1, store=True)
The depends is what triggers (any time you change the id_A field in the record) the compute method, if you dont store it in the DB it will re-calculate every time you open a view that contains the record.
I have this method in my Product model that does what I need:
def self.available
available = []
Product.all.each do |product|
if product.quantity > product.sales.sum(:quantity)
available << product
end
end
return available
end
But, I am wondering if there is a more efficient way to do this, maybe with only one call to the database.
Well you might try:
Product.where("products.quantity > Coalesce((select sum(s.quantity) from sales s where s.product_id = products.id), 0)")
This creates a number of queries equal to the number of products you have due to the sum query. Here is a way I though of that will reduce database queries.
map = Sale.joins(:product)
.group("products.id", "products.quantity").sum(:quantity).to_a
Which will produce an array similar to
[[[1,20],30], [[2,45], 20]]
this corresponds to [[[product_id, product_quantity], sold_quantity ]]
Now loop over this array and compare the values.
available = []
map.each do |item|
if item[0][1] > item[1]
available << item[0][0]
end
end
Now that you have the available array populated, perform another query.
available = Product.where(id: available)
Now you got your same output in two queries instead of Product.count (N) number of queries. This solution can be inefficient sometimes but I will be updating it regularly if I had any ideas.
I have a model named Appointment which has a has_many :invoices association with Invoice.
Appointment also has a grand_total bigdecimal field, while Invoice has an amount bigdecimal field.
I'd like to create a query saying select all Appointments whose invoices' amounts sum is greater or equal to 50% of grand_total.
Actually I select all Appointments, loop them and perform that check as follows
#selected_appointments = []
Appointments.all.each do |appointment|
invoices_sum = BigDecimal.new(0)
appointment.invoices.each do |invoice|
invoices_sum += invoice.amount
end
if invoices_sum >= (appointment.grant_total/2)
#selected_appointments.push(appointment)
end
end
But this doesn't really seem to be to be a good option since it's really slow.
Can you give me some help?
I think this might help you:
Appointment.select("appointments.*, sum(invoice.amount) invoices_total")
.joins(:invoices)
.group(:appointment_id)
.having('invoices_total >= (appointment.grant_total/2)')