I created Checkout controller, evething good, but I always have empty shipment address. Here code
namespace Controllers
{
using System;
using System.Web.Mvc;
using Merchello.Core;
using Merchello.Core.Gateways.Payment;
using Merchello.Core.Models;
using Merchello.Core.Services;
using Merchello.Web;
using Models;
using Models.ViewModels;
using Umbraco.Core;
using Umbraco.Web.Mvc;
/// <summary>
/// Summary description for CheckoutController
///
/// Workflow
/// 1. Display Basket Items http://website/checkout
/// 2. Get Address information http://website/checkout/address
/// 3. Get Payment information http://website/checkout/payment
/// 4. Show Invoice http://website/checkout/invoice?inv=[machineEncryptedKey]
/// </summary>
[PluginController("MerchelloProductListExample")]
public class CheckoutController : MerchelloSurfaceContoller
{
private const int BasketPageId = 1111;
private const int PaymentInfoId = 1113;
private const int ReceiptId = 1117;
/// <summary>
/// Merchello audit log to capture the error events
///
/// </summary>
private AuditLogService _log = new AuditLogService();
/// <summary>
/// Constructor without current context
/// </summary>
public CheckoutController()
: this(MerchelloContext.Current)
{ }
/// <summary>
/// Constructor with current context
/// </summary>
/// <param name="merchelloContext"></param>
public CheckoutController(IMerchelloContext merchelloContext)
: base(merchelloContext)
{ }
/// <summary>
/// Display address form - for this example shipping and billing are the same
/// </summary>
/// <param name="addressType"></param>
/// <returns></returns>
[ChildActionOnly]
public ActionResult RenderAddressForm(AddressType addressType)
{
ViewBag.AddressType = addressType;
return PartialView("Address");
}
/// <summary>
/// Save address to both billing and shipping
/// The anonymous customer address is stored in extended column
/// on merchAnonymousCustomer
///
/// I save shipping address as an example. It isn't used anywhere since
/// there isn't anything in the Order which is considered the Shippable
/// piece of the process.
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public ActionResult SaveAddress(AddressModel model)
{
var address = model.ToAddress();
// address saved to extended data on table merchAnonymousCustom
Basket.SalePreparation().SaveBillToAddress(address);
Basket.SalePreparation().SaveShipToAddress(address);
return SavePayment();
// go to payment page - only the cash payment is installed
//return RedirectToUmbracoPage(PaymentInfoId);
}
/// <summary>
/// Save payment is really process payment in this cash system.
/// As this system doesn't take credit cards, add tax, or ship, those
/// pieces are purposely not shown.
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public ActionResult SavePayment()
{
// has error occurred
bool error = false;
// do we raise events
bool raiseEvents = false;
// payment attempt result information
IPaymentResult attempt = null;
Guid paymentMethodGUID = new Guid("d37b53dd-78c6-40cd-969d-d1877c52de0e");
// get payment method
var paymentMethod = Payment.GetPaymentGatewayMethodByKey(paymentMethodGUID).PaymentMethod;
// get customer, items
var preparation = base.Basket.SalePreparation();
// Save the payment method selection
preparation.SavePaymentMethod(paymentMethod);
// make sure there is a billing address - it can be empty - it just has to exist
if (!preparation.IsReadyToInvoice()) return RedirectToUmbracoPage(BasketPageId);
// AuthorizePayment will save the invoice with an Invoice Number.
attempt = preparation.AuthorizePayment(paymentMethod.Key);
// if payment isn't successful, grab some information
if (!attempt.Payment.Success)
{
error = true;
// TBD - Not in Merchello yet
// Notification.Trigger("OrderConfirmationFailure", attempt, new[] { preparation.GetBillToAddress().Email });
_log.CreateAuditLogWithKey("Checkout failed - attempt payment failure", preparation.Customer.ExtendedData);
}
else
{
// trigger the order notification confirmation
Notification.Trigger("OrderConfirmation", attempt, new[] { preparation.GetBillToAddress().Email });
}
// grab final content page
var receipt = Umbraco.TypedContent(ReceiptId);
// redirect so that url has invoice number (encrypted) in address bar
// this feels clunky and unsafe but illustrative all the same
return
Redirect(string.Format("{0}?inv={1}", receipt.Url,
attempt.Invoice.Key.ToString().EncryptWithMachineKey()));
}
/// <summary>
/// Render Receipt - sale has been processed
/// </summary>
/// <param name="invoiceKey"></param>
/// <returns></returns>
[ChildActionOnly]
public ActionResult RenderReceipt(string invoiceKey)
{
Guid key;
if (Guid.TryParse(invoiceKey, out key))
{
var invoice = Services.InvoiceService.GetByKey(key);
return PartialView("CheckoutInvoice", invoice);
}
throw new InvalidOperationException();
}
/// <summary>
/// Render Invoice (name, address, items, total)
/// </summary>
/// <param name="invoice"></param>
/// <returns></returns>
private ActionResult RenderInvoice(IInvoice invoice)
{
return PartialView("CheckoutInvoice", invoice);
}
/// <summary>
/// When the payment is successful (i.e. Sale is complete)
/// the basket is empty again. So decide if we are getting
/// information from basket or invoice
/// </summary>
/// <returns></returns>
public ActionResult RenderInvoiceSummary(bool dataFromInvoice, Guid invoiceKey)
{
if (!dataFromInvoice)
{
return RenderIncompleteInvoiceSummary();
}
else
{
return RenderCompleteInvoiceSummary(invoiceKey);
}
}
/// <summary>
/// Renders checkout information in process so
/// gets everything from basket
/// </summary>
/// <returns></returns>
private ActionResult RenderIncompleteInvoiceSummary()
{
var model = new CheckoutViewModel();
if ((base.Basket != null) &&
(base.Basket.SalePreparation() != null))
{
if (base.Basket.SalePreparation().GetBillToAddress() != null)
{
model.CustomerName = base.Basket.SalePreparation().GetBillToAddress().Name ?? "";
model.CustomerAddress = new AddressModel();
model.CustomerAddress.Email = base.Basket.SalePreparation().GetBillToAddress().Email ?? "";
model.CustomerAddress.Address1 = base.Basket.SalePreparation().GetBillToAddress().Address1 ?? "";
model.CustomerAddress.Locality = base.Basket.SalePreparation().GetBillToAddress().Locality ?? "";
model.CustomerAddress.CountryCode = base.Basket.SalePreparation().GetBillToAddress().CountryCode ?? "";
model.CustomerAddress.PostalCode = base.Basket.SalePreparation().GetBillToAddress().PostalCode ?? "";
model.CustomerAddress.Region = base.Basket.SalePreparation().GetBillToAddress().Region ?? "";
}
if (base.Basket.SalePreparation().GetPaymentMethod() != null)
{
model.PaymentType = base.Basket.SalePreparation().GetPaymentMethod().Name ?? "";
}
if (base.Basket.Items != null)
{
model.Items = base.Basket.Items;
}
model.TotalBasketPrice = base.Basket.TotalBasketPrice;
}
return PartialView("CheckoutSummary", model);
}
/// <summary>
/// Renders checkout information after sale is done, so gets
/// everything from invoice. Basket is now empty once again.
/// </summary>
/// <param name="invoiceKey"></param>
/// <returns></returns>
private ActionResult RenderCompleteInvoiceSummary(Guid invoiceKey)
{
var invoice = Services.InvoiceService.GetByKey(invoiceKey);
var model = new CheckoutViewModel();
// init objects
model.CustomerAddress = new AddressModel();
model.Items = new LineItemCollection();
var customeraddress = new AddressModel();
model.CustomerName = invoice.BillToName ?? "";
customeraddress.Email = invoice.BillToEmail ?? "";
customeraddress.Address1 = invoice.BillToAddress1 ?? "";
customeraddress.Locality = invoice.BillToLocality ?? "";
customeraddress.CountryCode = invoice.BillToCountryCode ?? "";
customeraddress.PostalCode = invoice.BillToPostalCode ?? "";
customeraddress.Region = invoice.BillToRegion ?? "";
model.CustomerAddress = customeraddress;
//model.PaymentType = invoice.;
model.Items = invoice.Items;
model.TotalBasketPrice = invoice.Total;
return PartialView("CheckoutSummary", model);
}
}
private void SaveApprovedShipmentRateQuote(Guid shipMethodKey)
{
// if (!ModelState.IsValid) return CurrentUmbracoPage();
var shippingAddress = Basket.SalePreparation().GetShipToAddress();
// if (shippingAddress == null) return RedirectToUmbracoPage(GetContentIdByContentName(ShipRateQuotePage));
// Get the shipment again
var shipment = Basket.PackageBasket(shippingAddress).FirstOrDefault();
// Clear any previously saved quotes (eg. the user went back to their basket and started the process over again).
Basket.SalePreparation().ClearShipmentRateQuotes();
// get the quote using the "approved shipping method"
var quote = shipment.ShipmentRateQuoteByShipMethod(shipMethodKey);
// save the quote
Basket.SalePreparation().SaveShipmentRateQuote(quote);
// return SavePayment(); // Proceed to step 3
}
You can think of the the SaveShipToAddress method as basically a temporary storage mechanism for holding the address as you progress through the checkout workflow.
When you create a shipment, the address becomes the destination address for the shipment which is then serialized and added to the shipment line item of the invoice when created. This is so the original quote can be compared to the actual shipping cost, if for example the order was later broken into more than a single shipment , for example in a back order situation.
@Rusty I dont know what happens in my case, shipping address is cleaned.
I am working on a Paypal controller. This is my code with comments of the issue:
var preparation = Basket.SalePreparation();
preparation.RaiseCustomerEvents = false;
// Clear any previously saved quotes (eg. the user went back to their basket and started the process over again).
preparation.ClearShipmentRateQuotes();
var shippingAddress = Basket.SalePreparation().GetShipToAddress();
//Here shippingAddress has all the shipping fields filled OK
// Get the shipment again
var shipment = Basket.PackageBasket(shippingAddress).FirstOrDefault();
//Here shipment has all the fields unfilled
//Its weird because the only fiels filled are FromName ("Default Warehouse") and "Phone" field
//Filling again the lost values
shipment.FromName = shippingAddress.Name;
shipment.FromAddress1 = shippingAddress.Address1;
shipment.FromAddress2 = shippingAddress.Address2;
shipment.FromCountryCode = shippingAddress.CountryCode;
shipment.FromIsCommercial = shippingAddress.IsCommercial;
shipment.FromLocality = shippingAddress.Locality;
shipment.FromOrganization = shippingAddress.Organization;
shipment.FromPostalCode = shippingAddress.PostalCode;
shipment.FromRegion = shippingAddress.Region;
shipment.Phone = shippingAddress.Phone;
// get the quote using the "approved shipping method"
var quote = shipment.ShipmentRateQuoteByShipMethod(model.ShipMethodKey);
//quote.Shippment has all the fields unfilled again
// save the quote
Basket.SalePreparation().SaveShipmentRateQuote(quote);
@Rusty "shipment" is not null, it has all the fields blank but FromName ("Default Warehouse") and "Phone" field with the phone entered.
May it be something about the cache on basket? i call the method from angularjs after calling save address from angularjs too. But its weird because
"shippingAddress" object has all the fields filled...
Its like the Basket.PackageBasket is not copying the fields from shippingAdress object to shipment object.
And after manually filling them (as you can see in my code), calling to ShipmentRateQuoteByShipMethod copies the shipment to quote.Shipment but not the values. ¿?
Try using the overload of ShipmentRateQuoteByShipMethod with the cache parameter set to false. Depending on what you have going on the aysnc calls, it may be the quote (and thus the shipment in the quote) is being cached and never updating .
Worked for me as well - man what I've been trying to fix this issue. One would think that shippingManager.ClearShipmentRateQuotes(); would clear stuff out?
Shipping address is empty
I created Checkout controller, evething good, but I always have empty shipment address. Here code
{ using System; using System.Web.Mvc; using Merchello.Core; using Merchello.Core.Gateways.Payment; using Merchello.Core.Models; using Merchello.Core.Services; using Merchello.Web; using Models; using Models.ViewModels; using Umbraco.Core; using Umbraco.Web.Mvc;
}
In Partial view I have
}
Why this don`t works?
I checked value address and it is not empty and AddressType=Shipping
I found solution, I didn`t add this function
private void SaveApprovedShipmentRateQuote(Guid shipMethodKey) { // if (!ModelState.IsValid) return CurrentUmbracoPage();
@Anton - exactly correct.
You can think of the the SaveShipToAddress method as basically a temporary storage mechanism for holding the address as you progress through the checkout workflow.
When you create a shipment, the address becomes the destination address for the shipment which is then serialized and added to the shipment line item of the invoice when created. This is so the original quote can be compared to the actual shipping cost, if for example the order was later broken into more than a single shipment , for example in a back order situation.
@Rusty I dont know what happens in my case, shipping address is cleaned. I am working on a Paypal controller. This is my code with comments of the issue:
Is the shipment null or just the destination address in the shipment? If the shipment is null it could be that the country code was not recognized ...
Other than that, there must be something else going on in the application as the address is readonly in the packaging strategy.
https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Core/Strategies/Packaging/DefaultWarehousePackagingStrategy.cs
passed to base:
https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Core/Strategies/Packaging/PackagingStrategyBase.cs
@Rusty "shipment" is not null, it has all the fields blank but FromName ("Default Warehouse") and "Phone" field with the phone entered.
May it be something about the cache on basket? i call the method from angularjs after calling save address from angularjs too. But its weird because "shippingAddress" object has all the fields filled...
Its like the Basket.PackageBasket is not copying the fields from shippingAdress object to shipment object.
And after manually filling them (as you can see in my code), calling to ShipmentRateQuoteByShipMethod copies the shipment to quote.Shipment but not the values. ¿?
Try using the overload of
ShipmentRateQuoteByShipMethod
with the cache parameter set tofalse
. Depending on what you have going on the aysnc calls, it may be the quote (and thus the shipment in the quote) is being cached and never updating .Worked for me as well - man what I've been trying to fix this issue. One would think that shippingManager.ClearShipmentRateQuotes(); would clear stuff out?
ClearShipmentRateQuotes() should clear it out ... I'll have to add some tests to confirm that.
Thanks @Rusty, that made the trick!
is working on a reply...