How can i add additional information to the Magento Packingslip PDF. I am using integrated label paper, so i would like to add repeat the customers delivery address at the footer and also, some details like total quantity of items in the order and the cost of the items in the order. I am currently modifying local files in: Mage/Sales/Model/Order/Pdf/ but I have only managed to change to font.
EDIT:
Okay, i have made good progress and added most of the information i need, however, i have now stumbled across a problem.. I would like to get the total weight of all items in each order and Quantity.
I am using this code in the shipment.php:
Under the foreach (This is useful in case an order has a split delivery - as you can have one order with multiple "shipments" So this is why I have the code after here:
foreach ($shipment->getAllItems() as $item){
if ($item->getOrderItem()->getParentItem()) {
continue;
...
then I have this further down:
$shippingweight=0;
$shippingweight= $item->getWeight()*$item->getQty();
$page->drawText($shippingweight . Mage::helper('sales'), $x, $y, 'UTF-8');
This is great for Row totals. But not for the whole shipment. What I need to do is have this bit of code "added up" for each item in the shipment to create the total Weight of the whole shipment. It is very important that I only have the total of the shipment - not the order as I will be splitting shipments.
Almost at the end of getPdf(...) in Mage_Sales_Model_Order_Pdf_Shipment you will find the code that inserts new pages (lines 93-94 in 1.5.0.1)
if($this->y<15)
$page = $this->newPage(....)
happening in a for-loop.
You can change the logic here to make it change page earlier to make room for your extra information and then add it before the page shift. You should also place code after the for-block if you want it to appear on the last page as well.
Note: You should not change files in app/code/code/Mage directly. Instead, you should place your changed files under app/code/local/Mage using the same folder structure. That way your changes won't accidently get overwritten in an upgrade.
This should get you the total number of items in your order (there might be a faster way but don't know it off the top of my head):
$quote = Mage::getModel('sales/quote')->load($order->getQuoteId());
$itemsCount = $quote->getItemsSummaryQty();
For invoice PDF
at app/code/local/Mage/Sales/Model/Order/Pdf/Items/Invoice/Default.php
add this before $lineBlock
$product = Mage::getModel('catalog/product')->loadByAttribute('sku', $this->getSku($item), array('weight'));
$lines[][] = array(
'text' => 'Weight: '. $product->getData('weight')*1 .'Kg/ea. Total: ' .$product->getData('weight')*$item->getQty() . 'Kg',
'feed' => 400
);
Related
From the SingleProductViewModel, what is the best way to access prices for the variants associated with the product? From the documentation page linked above, I see that SingleProductViewModel contains a Product object, but I'm not sure how to use that to get prices of variants. (I can't find a listing of properties for the Product object).
Here is my specific use case: I have a Hotcakes Category Viewer and I'd like each product listed to display the range of prices for all variants of that product, rather than just the price for the main product. For example, a fedora product would display price as "$10 - $30" if the product contained variants with prices of $10, $20, and $30. I happen to be using the "simple" view of the category viewer, so am expecting to implement this in _RenderSingleProductSimple.cshtml, however I'm interested in using this for other category views, too.
Thanks in advance.
From what I've seen, most people will change their viewset to say something like "Starting at [PRICE]" or "As Low As [PRICE]" when there is a variant detected.
If you'd like to show the full range of prices, this can be done too, but you should know that depending on how many products that have variants and how many variants overall in the view, this could result in a negative performance impact on the site. How much impact is seen could range from negligible to undesirable.
The documentation you mentioned includes information about the Item property of the SingleProductViewModel class. This property includes all of the variant information you'd be looking for.
So, what you could do is use the Item.HasVariants property to determine if you need to have a different label. If that returns true, you can then iterate through the Item.Variants property to get all of the prices and find the lowest and highest ones to display.
Thanks #Will Strohl, that is helpful information.
I've put together the following code which seems to be achieving the original aim. Note that I said "variants" in the question, and these are product variants in our implementation, however we are achieving price adjustments for the variants via product options, so the code below looks at Model.Item.Options rather than Model.Item.Variants. Also, regarding price, I ignored user price details that weren't relevant to our implementation, and so used Model.Item.ListPrice rather than Model.UserPrice.DisplayPrice.
<div class="hc-recprice">
#{
string priceToDisplay = "";
if (Model.Item.HasOptions()){
decimal minPrice = Decimal.MaxValue;
decimal maxPrice = Decimal.MinValue;
decimal oiPrice = 0;
Hotcakes.Commerce.Catalog.OptionList options = Model.Item.Options;
foreach (Hotcakes.Commerce.Catalog.Option o in options){
foreach (Hotcakes.Commerce.Catalog.OptionItem oi in o.Items){
oiPrice = Model.Item.ListPrice + oi.PriceAdjustment;
if (oiPrice < minPrice) {
minPrice = oiPrice;
}
if (oiPrice > maxPrice) {
maxPrice = oiPrice;
}
}
}
if(minPrice == maxPrice){
priceToDisplay = string.Format("{0:C0}", minPrice);
} else {
priceToDisplay = string.Format("{0:C0}", minPrice) + " - " + string.Format("{0:C0}", maxPrice);
}
} else {
priceToDisplay = string.Format("{0:C0}", Model.Item.ListPrice);
}
#Html.Raw(priceToDisplay)
}
</div>
I am selling drinks in bottles that have a deposit price. The customer has to pay that amount, but can get it back by bringing the bottles to a supermarket. I would like to show the product price without the deposit in the product pages, but when the user checks out his cart, this amount needs to be added to the cart for each item having a deposit. Here are some extra info:
Not all products have a deposit
The deposit price depends on the product
I managed to add a "deposit" field to the backoffice in the product page:
http://oi57.tinypic.com/6p9s80.jpg
But it seems that changing the whole workflow of the cart check out would be quite a pain. Is there any easy way to achieve this task?
Thanks !
easiest way to do what you want it is using "product attributes combinations" + little code changes, step by step:
create product attribute e.g. "deposit"
add value to it, any,e.g. "yes"
in product create combination with "deposit: yes", set price impact value as you need, e.g. "$10" and set it as default combination
to show on product page price without "deposit" change in themes/themename/js/product.js in function updatePrice() find lines
// Apply combination price impact
// 0 by default, +x if price is inscreased, -x if price is decreased
basePriceWithoutTax = basePriceWithoutTax + +combination.price;
and wrap it into condition:
if( your_attribute_id != combination.attributes[0] ) {
basePriceWithoutTax = basePriceWithoutTax + +combination.price;
}
but in cart you will see full price.
UPD:
I see no good way to do it in template, without core changes, so if you need it (solution also not ideal)
file controllers/front/CategoryController.php (use override) method assignProductList() change code block to next view:
foreach ($this->cat_products as &$product) {
if ($product['id_product_attribute'] && isset($product['product_attribute_minimal_quantity']))
$product['minimal_quantity'] = $product['product_attribute_minimal_quantity'];
$combination = new Combination($product['id_product_attribute']);
$attributes = $combination->getAttributesName($this->context->language->id);
foreach ($attributes as $attribute) {
if(your_attribute_id == $attribute['id_attribute'])
$product['price_tax_exc'] -= $combination->price;
}
}
you will need to repeat it for all lists controllers that you use (and you can do not use foreach but access to array element by index like you already did), in any case solution very project specific, just quick fix, in common case be better use other ways.
First the introduction, in case there's is a better approach: I have a product table with *product_id* and stock, where stock can be as big as 5000 or 10000, I need to create a list (in another table) where I have a row for each item, this is, if a *propduct_id* has stock 1000 I'll have 1000 rows with this *product_id*, and plus, this list needs to be random.
I chose a PHP (symfony2) solution, as I found how to get a random single product_id based on stock or even how to random order the product list, but I didn't find how to "multiply" this rows by stock.
Now, the main problem:
So, in PHP it's no so difficult, get product_id list, "multiply" by stock and shuffle, the problem comes when I want to save:
If I use $em->flush every 100 records or more I get a memory overflow after a while
If I use $em->flush in every record it takes ages to save
This is my code to save which maybe you can improve:
foreach ($huge_random_list as $indice => $id_product)
{
$preasignacion = new ListaPreasignacion();
$preasignacion->setProductId($id_product);
$preasignacion->setOrden($indice+1);
$em->persist($preasignacion);
if ($indice % 100 == 0) $em->flush();
}
$em->flush();
Edit with final solution based on #Pazi suggestion:
$conn = $em->getConnection();
foreach ($huge_random_list as $indice => $id_product)
{
$conn->executeUpdate("insert into product_list(product_id, order) "
." values({$id_product}, {$indice})");
}
I would suggest to abstain from doctrine ORM and use the DBAL connection an pure sql queries for this purpose. I do this always in my applications, where I have to store much data in short time. Doctrine adds too much overhead with objects, checks and dehydrating. You can retrieve the DBAL connection via the DI container. For example in a contoller:
conn = $this->get('database_connection');
Read more about DBAL
I am missing the SQL out of this to Bulk update attributes by SKU/UPC.
Running EE1.10 FYI
I have all the rest of the code working but I"m not sure the who/what/why of
actually updating our attributes, and haven't been able to find them, my logic
is
Open a CSV and grab all skus and associated attrib into a 2d array
Parse the SKU into an entity_id
Take the entity_id and the attribute and run updates until finished
Take the rest of the day of since its Friday
Here's my (almost finished) code, I would GREATLY appreciate some help.
/**
* FUNCTION: updateAttrib
*
* REQS: $db_magento
* Session resource
*
* REQS: entity_id
* Product entity value
*
* REQS: $attrib
* Attribute to alter
*
*/
See my response for working production code. Hope this helps someone in the Magento community.
While this may technically work, the code you have written is just about the last way you should do this.
In Magento, you really should be using the models provided by the code and not write database queries on your own.
In your case, if you need to update attributes for 1 or many products, there is a way for you to do that very quickly (and pretty safely).
If you look in: /app/code/core/Mage/Adminhtml/controllers/Catalog/Product/Action/AttributeController.php you will find that this controller is dedicated to updating multiple products quickly.
If you look in the saveAction() function you will find the following line of code:
Mage::getSingleton('catalog/product_action')
->updateAttributes($this->_getHelper()->getProductIds(), $attributesData, $storeId);
This code is responsible for updating all the product IDs you want, only the changed attributes for any single store at a time.
The first parameter is basically an array of Product IDs. If you only want to update a single product, just put it in an array.
The second parameter is an array that contains the attributes you want to update for the given products. For example if you wanted to update price to $10 and weight to 5, you would pass the following array:
array('price' => 10.00, 'weight' => 5)
Then finally, the third and final attribute is the store ID you want these updates to happen to. Most likely this number will either be 1 or 0.
I would play around with this function call and use this instead of writing and maintaining your own database queries.
General Update Query will be like:
UPDATE
catalog_product_entity_[backend_type] cpex
SET
cpex.value = ?
WHERE cpex.attribute_id = ?
AND cpex.entity_id = ?
In order to find the [backend_type] associated with the attribute:
SELECT
backend_type
FROM
eav_attribute
WHERE entity_type_id =
(SELECT
entity_type_id
FROM
eav_entity_type
WHERE entity_type_code = 'catalog_product')
AND attribute_id = ?
You can get more info from the following blog article:
http://www.blog.magepsycho.com/magento-eav-structure-role-of-eav_attributes-backend_type-field/
Hope this helps you.
I use jqGrid to display data which is retrieved using NHibernate. jqGrid does paging for me, I just tell NHibernate to get "count" rows starting from "n".
Also, I would like to highlight specific record. For example, in list of employees I'd like a specific employee (id) to be shown and pre-selected in table.
The problem is that this employee may be on non-current page. E.g. I display 20 rows from 0, but "highlighted" employee is #25 and is on second page.
It is possible to pass initial page to jqGrid, so, if I somehow use NHibernate to find what page the "highlighted" employee is on, it will just navigate to that page and then I'll use .setSelection(id) method of jqGrid.
So, the problem is narrowed down to this one: given specific search query like the one below, how do I tell NHibernate to calculate the page where the "highlighted" employee is?
A sample query (simplified):
var query = Session.CreateCriteria<T>();
foreach (var sr in request.SearchFields)
query = query.Add(Expression.Like(sr.Key, "%" + sr.Value + "%"));
query.SetFirstResult((request.Page - 1) * request.Rows)
query.SetMaxResults(request.Rows)
Here, I need to alter (calculate) request.Page so that it points to the page where request.SelectedId is.
Also, one interesting thing is, if sort order is not defined, will I get the same results when I run the search query twice? I'd say that SQL Server may optimize query because order is not defined... in which case I'll only get predictable result if I pull ALL query data once, and then will programmatically in C# slice the specified portion of query results - so that no second query occur. But it will be much slower, of course.
Or, is there another way?
Pretty sure you'd have to figure out the page with another query. This would surely require you to define the column to order by. You'll need to get the order by and restriction working together to count the rows before that particular id. Once you have the number of rows before your id, you can figure what page you need to select and perform the usual paging query.
OK, so currently I do this:
var iquery = GetPagedCriteria<T>(request, true)
.SetProjection(Projections.Property("Id"));
var ids = iquery.List<Guid>();
var index = ids.IndexOf(new Guid(request.SelectedId));
if (index >= 0)
request.Page = index / request.Rows + 1;
and in jqGrid setup options
url: "${Url.Href<MyController>(c => c.JsonIndex(null))}?_SelectedId=${Id}",
// remove _SelectedId from url once loaded because we only need to find its page once
gridComplete: function() {
$("#grid").setGridParam({url: "${Url.Href<MyController>(c => c.JsonIndex(null))}"});
},
loadComplete: function() {
$("#grid").setSelection("${Id}");
}
That is, in request I lookup for index of id and set page if found (jqGrid even understands to display the appropriate page number in the pager because I return the page number to in in json data). In grid setup, I setup url to include the lookup id first, but after grid is loaded I remove it from url so that prev/next buttons work. However I always try to highlight the selected id in the grid.
And of course I always use sorting or the method won't work.
One problem still exists is that I pull all ids from db which is a bit of performance hit. If someone can tell how to find index of the id in the filtered/sorted query I'd accept the answer (since that's the real problem); if no then I'll accept my own answer ;-)
UPDATE: hm, if I sort by id initially I'll be able to use the technique like "SELECT COUNT(*) ... WHERE id < selectedid". This will eliminate the "pull ids" problem... but I'd like to sort by name initially, anyway.
UPDATE: after implemented, I've found a neat side-effect of this technique... when sorting, the active/selected item is preserved ;-) This works if _SelectedId is reset only when page is changed, not when grid is loaded.
UPDATE: here's sources that include the above technique: http://sprokhorenko.blogspot.com/2010/01/jqgrid-mvc-new-version-sources.html