コード例 #1
0
        /// <summary>
        /// Creates a collection of shipments for the current basket
        /// </summary>   
        public override IEnumerable<IShipment> PackageShipments()
        {
            // filter basket items for shippable items
            var shippableVisitor = new ShippableProductVisitor();
            LineItemCollection.Accept(shippableVisitor);

            if(!shippableVisitor.ShippableItems.Any()) return new List<IShipment>();

            // the origin address will be the default warehouse
            // For the initial version we are only exposing a single warehouse
            var warehouse = MerchelloContext.Services.WarehouseService.GetDefaultWarehouse();
            var origin = warehouse.AsAddress();

            //For the initial version we are only exposing a single shipment
            var shipment = new Shipment(origin, Destination)
                {
                    VersionKey = VersionKey // this is used in cache keys
                };

            // get the variants for each of the shippable line items
            var variants =
                   MerchelloContext.Services.ProductVariantService.GetByKeys(
                       shippableVisitor.ShippableItems.Select(x => x.ExtendedData.GetProductVariantKey()).Where(x => !Guid.Empty.Equals(x))
                       ).ToArray();

            foreach (var lineItem in shippableVisitor.ShippableItems)
            {
                // We need to know what Warehouse Catalog this product is associated with for shipping and inventory
                var variant = variants.FirstOrDefault(x => x.Key.Equals(lineItem.ExtendedData.GetProductVariantKey()));
                if(variant == null) throw new InvalidOperationException("This packaging strategy cannot handle null ProductVariants");

                if (variant.CatalogInventories.FirstOrDefault() == null)
                {
                    LogHelper.Error<ShippableProductVisitor>("ProductVariant marked as shippable was not assoicated with a WarehouseCatalog.  Product was: " + variant.Key.ToString() + " -  " + variant.Name, new InvalidDataException());
                }
                else
                {
                    lineItem.ExtendedData.SetValue("merchWarehouseCatalogKey", variant.CatalogInventories.First().CatalogKey.ToString());
                    shipment.Items.Add(lineItem);
                }

            }

            return new List<IShipment> { shipment };
        }
コード例 #2
0
ファイル: InvoiceTests.cs プロジェクト: ProNotion/Merchello
        public void Can_Create_A_Customer_Invoice_And_Order()
        {
            // Adding the shipmethod is typically done in the back office through the UI.
            // Interested in the use case to dynamically add theses?
            var key = Constants.ProviderKeys.Shipping.FixedRateShippingProviderKey;
            var defaultCatalogKey = Constants.DefaultKeys.Warehouse.DefaultWarehouseCatalogKey;

            // this would have to be done through the back office as it uses an internal service
            var us = MerchelloContext.Current.Services.StoreSettingService.GetCountryByCode("US");
            var usCountry = new ShipCountry(defaultCatalogKey, us);
            ((ServiceContext)MerchelloContext.Current.Services).ShipCountryService.Save(usCountry);

            // we can use this later.
            var rateTableProvider = (FixedRateShippingGatewayProvider)MerchelloContext.Current.Gateways.Shipping.GetProviderByKey(key);

            // again usually done in the back office
            if (!rateTableProvider.ShipMethods.Any())
            {
                // creates the rate table for ship rate quotes
                var gwShipmeMethod = (FixedRateShippingGatewayMethod)rateTableProvider.CreateShipMethod(FixedRateShippingGatewayMethod.QuoteType.VaryByWeight, usCountry, "Ground (Vary by Weight)");
                gwShipmeMethod.RateTable.AddRow(0, 10, 5);
                gwShipmeMethod.RateTable.AddRow(10, 15, 10); // total weight should be 10M so we should hit this tier
                gwShipmeMethod.RateTable.AddRow(15, 25, 25);
                gwShipmeMethod.RateTable.AddRow(25, 10000, 100);
                rateTableProvider.SaveShippingGatewayMethod(gwShipmeMethod);
            }

            // Get the persisted customer
            const string loginName = "rusty";

            var customerService = MerchelloContext.Current.Services.CustomerService;

            var customer = customerService.GetByLoginName(loginName)
                           ?? customerService.CreateCustomerWithKey(loginName, "Rusty", "Swayne", "*****@*****.**");

            // I'll use this for billing and shipping
            var billingAddress = new Address()
                                     {
                                         Name = "Mindfly Web Design Studio",
                                         Address1 = "114 W. Magnolia St. Suite 300",
                                         Locality = "Bellingham",
                                         Region = "WA",
                                         PostalCode = "98225",
                                         CountryCode = "US"
                                     };

            // Most of the time this information is brought in from the IProductVariant - but the idea is you can
            // describe things on the fly
            var extendedData = new ExtendedDataCollection();
            // this is used to determine where a shipment originates
            extendedData.SetValue(Constants.ExtendedDataKeys.WarehouseCatalogKey, defaultCatalogKey.ToString());
            // items set to shippable
            extendedData.SetValue(Constants.ExtendedDataKeys.TrackInventory, "false");
            extendedData.SetValue(Constants.ExtendedDataKeys.Shippable, "true");
            extendedData.SetValue(Constants.ExtendedDataKeys.Weight, "1.25");
            extendedData.SetValue(Constants.ExtendedDataKeys.CurrencyCode, "USD");

            var item = new InvoiceLineItem(LineItemType.Product, "My product", "mySku", 2, 10M, extendedData);

            var invoiceService = MerchelloContext.Current.Services.InvoiceService;

            var invoice = invoiceService.CreateInvoice(Constants.DefaultKeys.InvoiceStatus.Unpaid);
            // I'd say we need to add a parameter to the service so we don't have to do this
            // http://issues.merchello.com/youtrack/issue/M-434
            ((Invoice)invoice).CustomerKey = customer.Key;

            // The version key is useful in some cases to invalidate shipping quotes or taxation calculations
            invoice.VersionKey = Guid.NewGuid();

            invoice.Items.Add(item);

            // at this point the invoice is not saved and we don't have an invoice number
            // however, we may want to quote shipping so we need a shipment

            // Shipment Statuses are new in 1.5.0
            var warehouse = MerchelloContext.Current.Services.WarehouseService.GetDefaultWarehouse();

            var shipmentStatus =
                MerchelloContext.Current.Services.ShipmentService.GetShipmentStatusByKey(
                    Constants.DefaultKeys.ShipmentStatus.Quoted);

            // since we know all the items in the invoice will be shipped we don't need to filter
            var shipment = new Shipment(shipmentStatus, warehouse.AsAddress(), billingAddress, invoice.Items);

            // since we already know the shipping provider we want from above we can do
            var quotes = rateTableProvider.QuoteShippingGatewayMethodsForShipment(shipment);

            // if we wanted Merchello to get quotes from all shipping providers we'd do the following
            // var quotes = shipment.ShipmentRateQuotes();

            if (quotes.Any())
            {
                // this check makes certain a quote was returned.  For example if the collection of items was outside the allowable
                // weight range, the provider would not return a quote.

                // Add the first quote to the invoice.

                invoice.Items.Add(quotes.FirstOrDefault().AsLineItemOf<InvoiceLineItem>());
            }

            // you do need to update the total ... this is usually done in the InvoiceBuilder in
            // instantiated by a SalesPreparation sub class
            var charges = invoice.Items.Where(x => x.LineItemType != LineItemType.Discount).Sum(x => x.TotalPrice);
            var discounts = invoice.Items.Where(x => x.LineItemType == LineItemType.Discount).Sum(x => x.TotalPrice);

            // total the invoice
            decimal converted;
            invoice.Total = decimal.TryParse((charges - discounts).ToString(CultureInfo.InvariantCulture), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture.NumberFormat, out converted) ? converted : 0;

            // Now we save the invoice since we have to have a real record of something to collect a payment against
            // This also generates the invoice number
            invoiceService.Save(invoice);

            Console.WriteLine(invoice.InvoiceNumber);

            // cash payment method
            var cashProvider = MerchelloContext.Current.Gateways.Payment.GetProviderByKey(Constants.ProviderKeys.Payment.CashPaymentProviderKey);

            if (cashProvider != null)
            {
                var cash = cashProvider.PaymentMethods.FirstOrDefault(); // default install has a single payment method "Cash"

                // I usually Authorize a cash payment if taken online since we don't really see the money.  Capture is used
                // when the money comes in.  In your inquiry, it looks like you are assuming the money is in hand at the
                // time of the purchase, so we'll use AuthorizeCapture straight away.

                var attempt = invoice.AuthorizeCapturePayment(cash.Key);

                if (! attempt.Payment.Success)
                {
                    // handle the error
                }

                // otherwise you'll notice
                var approved = attempt.ApproveOrderCreation; // equals true

                // the order will be automatically created by the event handler in Merchello.Core.Gateways.GatewayEvents

                // however in this test I don't have the event wired up so I have to do it manuall
                if (approved)
                {
                   var order = invoice.PrepareOrder();
                   MerchelloContext.Current.Services.OrderService.Save(order);

                    var items = order.Items;
                }
            }
            // Cash provider is not active
        }
コード例 #3
0
        /// <summary>
        /// Creates a collection of shipments for the current basket
        /// </summary>
        /// <returns>
        /// A collection of <see cref="IShipment"/>.
        /// </returns>
        public override IEnumerable<IShipment> PackageShipments()
        {
            // All packaged shipments will start with a shipment status of "Quoted" as these are being used for the Shipment Rate Quote
            // NOTE:  the "Packaging" status to indicate the shipment is physically being packaged/boxed up.
            var quoted = MerchelloContext.Services.ShipmentService.GetShipmentStatusByKey(Constants.DefaultKeys.ShipmentStatus.Quoted);

            // filter basket items for shippable items
            var shippableVisitor = new ShippableProductVisitor();
            LineItemCollection.Accept(shippableVisitor);

            if (!shippableVisitor.ShippableItems.Any()) return new List<IShipment>();

            // the origin address will be the default warehouse
            // For the initial version we are only exposing a single warehouse
            var warehouse = MerchelloContext.Services.WarehouseService.GetDefaultWarehouse();
            var origin = warehouse.AsAddress();

            ////For the initial version we are only exposing a single shipment
            var shipment = new Shipment(quoted, origin, Destination)
                {
                    VersionKey = VersionKey // this is used in cache keys
                };

            // get the variants for each of the shippable line items
            var variants =
                   MerchelloContext.Services.ProductVariantService.GetByKeys(
                       shippableVisitor.ShippableItems
                       .Select(x => x.ExtendedData.GetProductVariantKey())
                       .Where(x => !Guid.Empty.Equals(x))).ToArray();

            foreach (var lineItem in shippableVisitor.ShippableItems)
            {
                // We need to know what Warehouse Catalog this product is associated with for shipping and inventory
                var variant = variants.FirstOrDefault(x => x.Key.Equals(lineItem.ExtendedData.GetProductVariantKey()));
                if (variant == null) throw new InvalidOperationException("This packaging strategy cannot handle null ProductVariants");

                if (variant.CatalogInventories.FirstOrDefault() == null)
                {
                    LogHelper.Error<ShippableProductVisitor>(
                        "ProductVariant marked as shippable was not assoicated with a WarehouseCatalog.  Product was: "
                        + variant.Key.ToString() + " -  " + variant.Name,
                        new InvalidDataException());
                }
                else
                {
                    // TODO this needs to be refactored to look at the entire shipment
                    // since products could be in multiple catalogs which could have
                    // opposing shippng rules and we have the destination address.
                    lineItem.ExtendedData.SetValue(
                        Constants.ExtendedDataKeys.WarehouseCatalogKey,
                        variant.CatalogInventories.First().CatalogKey.ToString());
                    shipment.Items.Add(lineItem);
                }
            }

            return new List<IShipment> { shipment };
        }