diff --git a/CHANGELOG.md b/CHANGELOG.md index 1131a6458..6c6e4ec75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,62 @@ # Changelog ## v3.6.0 [upcoming] +**cart** +* **Breaking**: Move all calculations to cart behaviour implementation + * By moving calculation responsibility, we enable different implementation possibilities for calculations like tax before or after discounts, tax on single item or sum and different tax rounding modes instead of having it hard-coded in the flamingo cart. + * All calculation functions on cart item, shipping item, delivery and cart are now public fields for which the values must be set by the cart behaviour implementation + * The `DefaultCartBehaviour` calculates all new fields accordingly + * Removed `ItemBuilder`, `DeliveryBuilder` and `Builder` since they didn't provide any meaningful functionality after removing the calculations. Please create structs directly. + * Changed the GraphQL cart model accordingly. + * To help with the migration there are sed commands for the following fields in `cart/migration.sed`: run `find . -type f -iname '*.go' -exec gsed -i -f migration.sed "{}" +;` + * Cart items + * | Old Function | New Field | + |----------------------------------------|--------------------------------------| + | RowPriceGrossWithDiscount() | RowPriceGrossWithDiscount | + | RowPriceGrossWithItemRelatedDiscount() | RowPriceGrossWithItemRelatedDiscount | + | RowPriceNetWithDiscount() | RowPriceNetWithDiscount | + | RowPriceNetWithItemRelatedDiscount() | RowPriceNetWithItemRelatedDiscount | + | TotalDiscountAmount() | TotalDiscountAmount | + | ItemRelatedDiscountAmount() | ItemRelatedDiscountAmount | + | NonItemRelatedDiscountAmount() | NonItemRelatedDiscountAmount | + * Shipping items + * | Old Function | New Field | + |----------------------------|-------------------------| + | TotalWithDiscountInclTax() | PriceGrossWithDiscounts | + | - | PriceNetWithDiscounts | + * Deliveries + * | Old Function | New Field | + |-----------------------------------|---------------------------------| + | SubTotalGross() | SubTotalGross | + | SubTotalNet() | SubTotalNet | + | SumTotalDiscountAmount() | TotalDiscountAmount | + | SumSubTotalDiscountAmount() | SubTotalDiscountAmount | + | SumNonItemRelatedDiscountAmount() | NonItemRelatedDiscountAmount | + | SumItemRelatedDiscountAmount() | ItemRelatedDiscountAmount | + | SubTotalGrossWithDiscounts() | SubTotalGrossWithDiscounts | + | SubTotalNetWithDiscounts() | SubTotalNetWithDiscounts | + | GrandTotal() | GrandTotal | + * Cart + * | Old Function | New Field | + |-----------------------------------|---------------------------------| + | GrandTotal() | GrandTotal | + | - | GrandTotalNet | + | SumShippingNet() | ShippingNet | + | SumShippingNetWithDiscounts() | ShippingNetWithDiscounts | + | SumShippingGross() | ShippingGross | + | SumShippingGrossWithDiscounts() | ShippingGrossWithDiscounts | + | SubTotalGross() | SubTotalGross | + | SubTotalNet() | SubTotalNet | + | SubTotalGrossWithDiscounts() | SubTotalGrossWithDiscounts | + | SubTotalNetWithDiscounts() | SubTotalNetWithDiscounts | + | SumTotalDiscountAmount() | TotalDiscountAmount | + | SumNonItemRelatedDiscountAmount() | NonItemRelatedDiscountAmount | + | SumItemRelatedDiscountAmount() | ItemRelatedDiscountAmount | + | SumAppliedGiftCards() | TotalGiftCardAmount | + | SumGrandTotalWithGiftCards() | GrandTotalWithGiftCards | + | - | GrandTotalNetWithGiftCards | + + ## v3.5.0 **general** * Switch to MIT License @@ -36,6 +92,7 @@ * **Breaking**: `Commerce_Cart_UpdateDeliveryShippingOptions` mutation responded with slice of `Commerce_Cart_DeliveryAddressForm` which was incorrect as we don't process any form data within the mutation. It responds now rightly only with `processed` state. * **Breaking**: Upgrade github.com/go-playground/form to v4, all types are fully compatible, but import paths have to be changed + **checkout** * Introducing Flamingo events on final states of the place order process * Introduce a max ttl for the checkout state machine to avoid polluting the redis with stale checkout processes, defaults to 2h diff --git a/cart/Changelog.md b/cart/Changelog.md deleted file mode 100644 index 2618dbd1a..000000000 --- a/cart/Changelog.md +++ /dev/null @@ -1,102 +0,0 @@ -# 14. June 2018 - -* Price Fields in Cartitems and Carttotals have been changed: - * Cartitem: - * Deleted (Dont use anymore): Price / DiscountAmount / PriceInclTax - * Now Existing: SinglePrice / SinglePriceInclTax / RowTotal / TaxAmount/ RowTotalInclTax / TotalDiscountAmount / ItemRelatedDiscountAmount / NonItemRelatedDiscountAmount / RowTotalWithItemRelatedDiscount / RowTotalWithItemRelatedDiscountInclTax / RowTotalWithDiscountInclTax - - * Carttotal: - * Deleted: DiscountAmount - * Now Existing: SubTotal / SubTotalInclTax / SubTotalInclTax /SubTotalWithDiscounts / SubTotalWithDiscountsAndTax / TotalDiscountAmount / TotalNonItemRelatedDiscountAmount - -# 17. April 2019 - -* Cart Item `UniqueID` is removed - * `Item.ID` is now supposed to be unique - * The combination `ID` + `DeliveryCode` is no longer required to identify a cart item - * For non-unique references of certain backend implementations the new field `Item.ExternalReference` can be used - -# 23. July 2019 -* Add general gift card support - * `cart.AppliedGiftCards` contains a list of applied gift cards - * Add convenience functions for gift card like `SumGrandTotalWithGiftCards()` and `HasAppliedGiftCards()` - -* Add support for gift cards in default payment selection handling - * Adds new public function `NewDefaultPaymentSelection` which will generate a basic payment selection - * Changed visibility of `NewSimplePaymentSelection` to private, please use `NewDefaultPaymentSelection` instead - * Update ChargeQualifier, add additional Reference string field - * Add support for multiple charges of the same type (unique Reference needed) - -# 8. August 2019 -* Renamed 'cart.BillingAdress' to 'cart.BillingAddress' - -# 15. August 2019 -* Removed `ShippingItem.DiscountAmount` - * Added `ShippingItem.AppliedDiscounts` - * ShippingItem now implements interface `WithDiscount` - -# 9. October 2019 -* Add `PlaceOrderWithCart` to `CartService` to be able to place an already fetched cart instead of triggering an additional call to the `CartReceiverService` - -# 18. December 2019 -* Introduce `UpdateItems` to `ModifyBehaviour`interface to reduce calls for updating items one by one -* Add helper function `GetDeliveryByItemID` -* Remove `itemID` and `deliveryCode` as parameters for `UpdateItem` as this information is part of the update command, respectively from the new helper - -# 7. January 2020 -* Add [Idempotency Key pattern](https://stripe.com/blog/idempotency) to the `PaymentSelection` - * PaymentSelection interface now offers new functions for receiving (`IdempotencyKey()`) / generating (`GenerateNewIdempotencyKey()`) a new Idempotency-Key - -# 10. January 2020 -* Changes AppliedCouponCodes in the cart to an own struct to be able to add some functions to it -* Quantity item adjustments know also contain a bool that indicates if the respective adjustment caused a change to the AppliedCouponCodes slice of the cart - * New template function to get if any of the currently stored adjustments caused a coupon code to be removed - -# 17. January 2020 -* Add `AppliedGiftCard` convenience function `Total()` - -# 10. February 2020 -* Add `additionalData` to `AddRequest` - * Breaking change: Update helper/builder function `BuildAddRequest` -* Breaking Change to `EventPublisher` interface, `PublishChangedQtyInCartEvent` and `PublishAddToCartEvent` now -include a cart as a parameter -* Breaking Change to behaviour of `AddToCartEvent` and `ChangedQtyInCartEvent`, they are now thrown after -the cart has been adjusted and written back to cache -* Events deferred from `ModifyBehaviour` are dispatched before `AddToCartEvent` and `ChangedQtyInCartEvent` -* The `AddToCartEvent` includes the current cart (with added product) -* The `ChangedQtyInCartEvent` includes the current cart (with updated quantities) -* Add Whitebox Test `TestCartService_CartInEvent` to check `AddToCartEvent` - -# 20. February 2020 - -* Mark `CartReceiverService.RestoreCart()` as deprecated, use `CartService.RestoreCart()` instead, - the cart adapter therefore needs to implement the `CompleteBehaviour` interface. -* Add `CartReceiverService.ModifyBehaviour()` to easily receive the current behaviour (guest/customer) - -* Add `CompleteBehaviour` interface which ensures that the cart adapter offers Complete / Restore functionality -* Add `CartService.CompleteCurrentCart()` and `CartService.RestoreCart()` which rely on the new `CompleteBehaviour` interface -* **Breaking**: Update `CartService.CancelOrder()` to use `CartService.RestoreCart()` instead of `CartReceiverService.RestoreCart()`, - if your cart supports completing/restoring please implement `CompleteBehaviour` interface -* Add `CartService.CancelOrderWithoutRestore()` to allow order cancellation without restoring the cart - -* Mark `GuestCartService.RestoreCart` as deprecated, will be replaced by `CompleteBehaviour` -* Mark `CustomerCartService.RestoreCart` as deprecated, will be replaced by `CompleteBehaviour` - -* Add mocks for all behaviours, you can use a specific one e.g. `&mocks.CompleteBehaviour{}` or the all in one `&mocks.AllBehaviour{}` - -* Update `InMemoryBehaviour` to fulfill the `CompleteBehaviour` interface (adds `Complete()`/`Restore()`) -* Update `InMemoryCartStorage`, add Mutex to be thread safe - -* Update `SimplePaymentFormService` to allow gift cards in the `PaymentSelection`, please use the - config `commerce.cart.simplePaymentForm.giftCardPaymentMethod`to specify the default payment method for gift cards - -* Add missing `product` module dependency to cart module - -# 13. May 2020 - -* Update pagination module configuration. Use "commerce.pagination" namespace for configuration now. -* search and product module configuration is using CueConfig - and therefore config options can be looked up in the commandline - -# 26. May 2020 - -* Change `ItemValidator` interface to require the decorated cart, so that implemented validators don't ave to load the cart again which can be the wrong cart after all. diff --git a/cart/Readme.md b/cart/Readme.md index 6d72a516f..74707f218 100644 --- a/cart/Readme.md +++ b/cart/Readme.md @@ -172,76 +172,71 @@ There are two principal ways of tax calculations (called vertical and horizontal * discounts are normally subtracted before tax calculation -* At least in germany the law doesn't force one algorithm over the other. In this cart module it is up to the CartService implementation what algorithm it uses to fill the tax. - -* Flamingo cart model calculates sums of prices and taxes using the "vertical" way - its doing this by basically adding the prices in the items up on delivery and cart levels. So if you want to use "horizontal" tax calculations the cartservice implementation need to make sure that the item prices are set correct (split correct with cent corrections - outgoing from the horizontal sums). - +* At least in germany the law doesn't force one algorithm over the other. In this cart module it is up to the cart behaviour implementation what algorithm it uses to fill the tax. ### Cartitems - price fields and method The Key with "()" in the list are methods and it is assumed as an invariant, that all prices in an item have the same currency. -| Key | Desc | Math Invariants | -|----------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------| -| SinglePriceGross | Single price of product (gross) (was SinglePrice) | | -| SinglePriceNet | (net) | | -| Qty | Qty | | -| RowPriceGross | (was RowTotal ) | RowPriceGross ~ SinglePriceGross * Qty | -| RowPriceNet | | RowPriceNet ~ SinglePriceNet * Qty | -| RowTaxes | Collection of (summed up) Taxes for that item row. | | -| TotalTaxAmount() | Sum of all Taxes for this Row | = RowPriceGross-RowPriceNet | -| AppliedDiscounts | List with the applied Discounts for this Item (There are ItemRelated Discounts and Discounts that are not ItemRelated (CartRelated). However it is important to know that at the end all DiscountAmounts are applied to an item (to make refunding logic easier later) | | -| TotalDiscountAmount() | Complete Discount for the Row. If the Discounts have no tax/duty (they can be considered as Gross). If they are applied from RowPriceGross or RowPriceNet depends on the calculations done in the cartservice implementation. | TotalDiscountAmount = Sum of AppliedDiscounts TotalDiscountAmount = ItemRelatedDiscountAmount +NonItemRelatedDiscountAmount | -| NonItemRelatedDiscountAmount() | | NonItemRelatedDiscountAmount = Sum of AppliedDiscounts where IsItemRelated = false | -| ItemRelatedDiscountAmount() | | ItemRelatedDiscountAmount = Sum of AppliedDiscounts where IsItemRelated = false | -| RowPriceGrossWithDiscount() | | RowPriceGross-TotalDiscountAmount() | -| RowPriceNetWithDiscount() | | | -| RowPriceGrossWithItemRelatedDiscount() | | RowPriceGross-ItemRelatedDiscountAmount() | -| RowPriceNetWithItemRelatedDiscount() | | | -| | | | +| Key | Desc | Math Invariants | +|--------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| +| SinglePriceGross | Single price of product (gross) (was SinglePrice) | | +| SinglePriceNet | Net price (excl. taxes) for the product | | +| Qty | Quantity | | +| RowPriceGross | Price incl. taxes for the whole Qty of products (was RowTotal) | SinglePriceGross * Qty | +| RowPriceGrossWithDiscount | Price incl. taxes with deducted discounts for the whole Qty of products. This is in most cases the final price for the customer to pay. | RowPriceGross-TotalDiscountAmount() | +| RowPriceGrossWithItemRelatedDiscount | Price incl. taxes with deducted item related discounts for the whole Qty of products | RowPriceGross-ItemRelatedDiscountAmount() | +| RowPriceNet | Price excl. taxes for the whole Qty of products | SinglePriceNet * Qty | +| RowPriceNetWithDiscount | The discounted net price for the whole Qty of products | | +| RowPriceNetWithItemRelatedDiscount | price excl. taxes with deducted item related discounts for the whole Qty of products | | +| RowTaxes | Collection of all taxes applied for the given Qty of products | | +| TotalTaxAmount() | sum of all applied taxes for the whole Qty of products | RowPriceGross-RowPriceNet | +| AppliedDiscounts | List with the applied Discounts for this Item (There are ItemRelated Discounts and Discounts that are not ItemRelated (CartRelated). However it is important to know that at the end all DiscountAmounts are applied to an item (to make refunding logic easier later) | | +| TotalDiscountAmount | Sum of all applied discounts (aka the savings for the customer) | Sum of AppliedDiscounts (ItemRelatedDiscountAmount + NonItemRelatedDiscountAmount) | +| ItemRelatedDiscountAmount | Sum of all itemrelated Discounts, e.g. promo due to product attribute | | +| NonItemRelatedDiscountAmount | Sum of non-itemrelated Discounts, e.g. a general promo | | [comment]: <> (use https://www.tablesgenerator.com/markdown_tables to update the table) ### Delivery - price fields and method -| Key | Desc | Math | -|-----------------------------------|------------------------------------------------------------------------------|------------------------------------------------------------------------| -| GrandTotal() | The final amount that need to be paid by the customer (Gross) | SubTotalGross() + ShippingItem.PriceGross + SumTotalDiscountAmount() | -| SubTotalGross() | Sum of items RowPriceGross | | -| SumRowTaxes() | List of the sum of the different RowTaxes (of cart items) | | -| SumTotalTaxAmount() | Sum of all applied item taxes including shipping taxes | | -| SubTotalNet() | Sum of items RowPriceNet | | -| SubTotalGrossWithDiscounts() | | SubTotalGross() + SumSubTotalDiscountAmount() | -| SubTotalNetWithDiscounts() | | SubTotalNet() + SumSubTotalDiscountAmount() | -| SumTotalDiscountAmount() | Sum off all discounts affecting the delivery (on cart items and shipping) | SumSubTotalDiscountAmount() + shippingItem.AppliedDiscounts.Sum() | -| SumSubTotalDiscountAmount() | Sum of all cart items discounts (without shipping) | Sum of items TotalDiscountAmount | -| SumNonItemRelatedDiscountAmount() | Sum of all discounts with IsItemRelated=false (including shipping discounts) | | -| SumItemRelatedDiscountAmount() | Sum of all discounts with IsItemRelated=true (including shipping discounts) | | +| Key | Desc | Math | +|---------------------------------|----------------------------------------------------------------------------------|------------------------------------------------------------------| +| GrandTotal | The final amount that need to be paid by the customer (Gross) | SubTotalGross + ShippingItem.PriceGross + TotalDiscountAmount | +| SubTotalGross | Sum of items RowPriceGross (without shipping/discounts) | | +| SubTotalGrossWithDiscounts | Sum of row gross prices reduced by the applied discounts | SubTotalGross() + SumSubTotalDiscountAmount() | +| SubTotalNetWithDiscounts | Sum of row net prices reduced by the net value of the applied discounts | SubTotalNet() + SumSubTotalDiscountAmount() | +| SubTotalNet | Sum of items RowPriceNet | | +| SumRowTaxes() | List of the sum of the different RowTaxes (of cart items) | | +| SumTotalTaxAmount() | Sum of all applied item taxes including shipping taxes | | +| TotalDiscountAmount | Sum off all discounts affecting the delivery (on cart items and shipping) | SumSubTotalDiscountAmount + shippingItem.AppliedDiscounts.Sum() | +| SubTotalDiscountAmount | Sum of all cart items discounts (without shipping) | Sum of items TotalDiscountAmount | +| NonItemRelatedDiscountAmount | Sum of discounts that are not related to the item (including shipping discounts) | | +| ItemRelatedDiscountAmount | Sum of discounts that are related to the item (including shipping discounts) | | ### Cart - price fields and method -| Key | Desc | Math | -|-----------------------------------|----------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------| -| GrandTotal() | The final amount that need to be paid by the customer (Gross) | Sum(RowPriceGrossWithDiscount) + Sum(shipping.TotalWithDiscountInclTax) + Sum(Totalitems) | -| Totalitems | List of (additional) Totalitems. Each have a certain type - you may want to show some of them in the frontend. | | -| SumShippingNet() | Sum of all shipping costs as Price | Sum of all deliveries shipping items PriceNet | -| SumShippingNetWithDiscounts() | Sum of all shipping costs with all shipping discounts | SumShippingNet() + Sum of all applied shipping discounts | -| SumShippingGross() | Sum of all shipping costs including tax | Sum of all deliveries shipping items PriceGross | -| SumShippingGrossWithDiscounts() | Sum of all shipping costs with all shipping discounts including tax | Sum of shipping items TotalWithDiscountInclTax() | -| SubTotalGross() | | Sum of deliveries SubTotalGross() | -| SumTaxes() | The total taxes of the cart - as list of Tax | | -| SumTotalTaxAmount() | The overall Tax of cart as Price | | -| SubTotalNet() | | Sum of deliveries SubTotalNet() | -| SubTotalGrossWithDiscounts() | | Sum of deliveries SubTotalGrossWithDiscounts() | -| SubTotalNetWithDiscounts() | | Sum of deliveries SubTotalNetWithDiscounts() | -| SumTotalDiscountAmount() | The overall applied discount | Sum of deliveries TotalDiscountAmount | -| SumNonItemRelatedDiscountAmount() | | Sum of deliveries NonItemRelatedDiscountAmount | -| SumItemRelatedDiscountAmount() | | Sum of deliveries ItemRelatedDiscountAmount | -| GetVoucherSavings() | Returns the sum of Totals from type voucher | | -| HasShippingCosts() | True if cart has in sum any shipping costs | | -| SumAppliedGiftCards() | | Sum of all Applied GiftCard amounts | -| SumGrandTotalWithGiftCards() | The final amount with the applied gift cards subtracted. If there are no gift cards, equal to GrandTotal() | GrandTotal() - SumAppliedGiftCards() | +| Key | Desc | Math | +|---------------------------------|----------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------| +| GrandTotal | The final amount that need to be paid by the customer (gross) | SubtotalGrossWithDiscounts + SumShippingGrossWithDiscounts + Sum(Totalitems) | +| Totalitems | List of (additional) Totalitems. Each have a certain type - you may want to show some of them in the frontend. | | +| SumShippingNet | Sum of all shipping costs | Sum of all deliveries shipping items PriceNet | +| SumShippingNetWithDiscounts | Sum of all shipping costs with all shipping discounts | SumShippingNet + Sum of all applied shipping discounts | +| SumShippingGross | Sum of all shipping costs including tax | Sum of all deliveries shipping items PriceGross | +| SumShippingGrossWithDiscounts | Sum of all shipping costs with all shipping discounts including tax | Sum of shipping items PriceGrossWithDiscounts | +| SubTotalGross | Sum of all delivery subtotals (without shipping / discounts) | Sum of deliveries SubTotalGross | +| SubTotalNet | Sum of all delivery net subtotals (without shipping / discounts) | Sum of deliveries SubTotalNet | +| SumTaxes() | The total taxes of the cart - as list of Tax | | +| SumTotalTaxAmount() | The overall Tax of cart | | +| SubTotalGrossWithDiscounts | Sum of row gross prices reduced by the applied discounts | Sum of deliveries SubTotalGrossWithDiscounts | +| SubTotalNetWithDiscounts | Sum of row net prices reduced by the net value of the applied discounts | Sum of deliveries SubTotalNetWithDiscounts | +| TotalDiscountAmount | Sum of all discounts (incl. shipping) | Sum of deliveries TotalDiscountAmount | +| NonItemRelatedDiscountAmount | Sum of discounts that are not related to the item (including shipping discounts) | Sum of deliveries NonItemRelatedDiscountAmount | +| ItemRelatedDiscountAmount | Sum of discounts that are related to the item (including shipping discounts) | Sum of deliveries ItemRelatedDiscountAmount | +| TotalGiftCardAmount | The part of GrandTotal which is paid by gift cards | | +| GrandTotalWithGiftCards | The final amount with the applied gift cards subtracted. If there are no gift cards, equal to GrandTotal. | GrandTotal - SumAppliedGiftCards | +| GetVoucherSavings() | Returns the sum of Totalitems of type voucher | | ### Typical B2C vs B2B usecases @@ -275,17 +270,11 @@ B2B use cases: ### Building a Cart with its deliveries and items -The domain concept is that the cart is returned and updated by the "Cartservice" - which is the main secondary port of the package that needs to be implemented (see below). -The implementations should use the provided "Builder" objects to build up Items, Deliveries and Carts. The Builder items ensure that the invariants are met and help with calculations of missing values. - -e.g. to build an item: -```go -builder := t.itemBuilderProvider() -item, err := builder.SetSinglePriceNet(priceDomain.NewFromInt(100, 100, "EUR")).SetQty(10).SetID("22").SetUniqueID("kkk").CalculatePricesAndTaxAmountsFromSinglePriceNet().Build() - -``` +The domain concept is that the cart is returned and updated by the "Cartservice" and the underlying modify behaviour, which is the main secondary port of the package that needs to be implemented (see below). +All calculations for the public price fields must be done by the modify behaviour implementation. ### About charges + If you have read the sections above you know about the different prices that are available at item, delivery and cart level and how they are calculated. There is something else that this cart model supports - we call it "charges". All the price amounts mentioned in the previous chapters represents the value of the items in the carts default currency. @@ -330,7 +319,7 @@ Most of the cart modification methods are part of the `ModifyBehaviour` interfac `ModifyBehaviour` interface - so in fact this interface needs to be implemented when writing an adapter as well. **in-memory cart adapter** -There is a "InMemoryAdapter" implementation as part of the package. It allows basic cart operations with a cart that is stored in memory. +There is a "DefaultCartBehaviour" implementation as part of the package. It allows basic cart operations with a cart that is stored in memory. Since the cart storage is not persisted in any way we currently recommend the usage only for demo / testing. The in memory adapter supports custom gift card / voucher logic by implementing the `GiftCardHandler` and `VoucherHandler` interfaces. diff --git a/cart/application/cartCache_test.go b/cart/application/cartCache_test.go index 07fcaf4e6..fe4b5506b 100644 --- a/cart/application/cartCache_test.go +++ b/cart/application/cartCache_test.go @@ -532,23 +532,16 @@ func TestCartSessionCache_DeleteAll(t *testing.T) { func getFixtureCartForTest(t *testing.T) *cart.Cart { t.Helper() - cartItemBuilder := cart.ItemBuilder{} - cartItemBuilder.SetSinglePriceGross(domain.NewFromInt(2050, 100, "EUR")).SetID("1").SetExternalReference("1ext").SetQty(2).CalculatePricesAndTaxAmountsFromSinglePriceGross() - item, err := cartItemBuilder.Build() - if err != nil { - t.Fatal("cannot build fixture cart for test!", err) - } - deliveryBuilder := cart.DeliveryBuilder{} - deliveryBuilder.AddItem(*item).SetDeliveryCode("code") - delivery, err := deliveryBuilder.Build() - if err != nil { - t.Fatal("cannot build delivery cart for test!", err) - } - cartBuilder := cart.Builder{} - cartBuilder.AddDelivery(*delivery).SetDefaultCurrency("EUR").SetIds("1", "1") - cartResult, err := cartBuilder.Build() - if err != nil { - t.Fatal("cannot build cart for test!", err) + item := cart.Item{ + ID: "1", + ExternalReference: "1ext", + Qty: 2, + SinglePriceGross: domain.NewFromInt(2050, 100, "EUR"), + SinglePriceNet: domain.NewFromInt(2050, 100, "EUR"), } + + delivery := cart.Delivery{DeliveryInfo: cart.DeliveryInfo{Code: "code"}, Cartitems: []cart.Item{item}} + cartResult := &cart.Cart{ID: "1", EntityID: "1", DefaultCurrency: "EUR", Deliveries: []cart.Delivery{delivery}} + return cartResult } diff --git a/cart/application/cartService_test.go b/cart/application/cartService_test.go index c75d163e4..43ced7416 100644 --- a/cart/application/cartService_test.go +++ b/cart/application/cartService_test.go @@ -759,15 +759,6 @@ func (m *MockGuestCartServiceWithModifyBehaviour) GetModifyBehaviour(context.Con storage, &MockProductService{}, flamingo.NullLogger{}, - func() *cartDomain.ItemBuilder { - return &cartDomain.ItemBuilder{} - }, - func() *cartDomain.DeliveryBuilder { - return &cartDomain.DeliveryBuilder{} - }, - func() *cartDomain.Builder { - return &cartDomain.Builder{} - }, nil, nil, nil, diff --git a/cart/domain/cart/address_test.go b/cart/domain/cart/address_test.go new file mode 100644 index 000000000..a6fdb2210 --- /dev/null +++ b/cart/domain/cart/address_test.go @@ -0,0 +1,27 @@ +package cart_test + +import ( + "testing" + + "flamingo.me/flamingo-commerce/v3/cart/domain/cart" + "github.com/stretchr/testify/assert" +) + +func TestAddress_FullName(t *testing.T) { + address := cart.Address{Firstname: "first", Lastname: "last"} + assert.Equal(t, "first last", address.FullName()) +} + +func TestAddress_IsEmpty(t *testing.T) { + address := cart.Address{} + assert.True(t, address.IsEmpty()) + + address = cart.Address{Firstname: "hey"} + assert.False(t, address.IsEmpty()) + + address = cart.Address{AdditionalAddressLines: []string{"foo"}} + assert.False(t, address.IsEmpty()) + + var nilAddress *cart.Address + assert.True(t, nilAddress.IsEmpty()) +} diff --git a/cart/domain/cart/cart.go b/cart/domain/cart/cart.go index dc201b513..22387ecc8 100644 --- a/cart/domain/cart/cart.go +++ b/cart/domain/cart/cart.go @@ -14,9 +14,6 @@ import ( ) type ( - // Provider should be used to create the cart Value objects - provider func() *Cart - // Cart Value Object (immutable data - because the CartService is responsible to return a cart). Cart struct { // ID is the main identifier of the cart @@ -55,6 +52,38 @@ type ( // AppliedGiftCards is a list of applied gift cards AppliedGiftCards []AppliedGiftCard + // AppliedGiftCardsAmount is the part of GrandTotal which is paid by gift cards + TotalGiftCardAmount domain.Price + // GrandTotalWithGiftCards is the final amount with the applied gift cards subtracted. + GrandTotalWithGiftCards domain.Price + // GrandTotalNetWithGiftCards is the corresponding net value to GrandTotalWithGiftCards + GrandTotalNetWithGiftCards domain.Price + // GrandTotal is the final amount that need to be paid by the customer (gross) + GrandTotal domain.Price + // GrandTotalNet is the corresponding net value to GrandTotal + GrandTotalNet domain.Price + // ShippingNet is the sum of all shipping costs + ShippingNet domain.Price + // ShippingNetWithDiscounts is the sum of all shipping costs with all shipping discounts + ShippingNetWithDiscounts domain.Price + // ShippingGross is the sum of all shipping costs including tax + ShippingGross domain.Price + // ShippingGrossWithDiscounts is the sum of all shipping costs with all shipping discounts including tax + ShippingGrossWithDiscounts domain.Price + // SubTotalGross is the sum of all delivery subtotals (without shipping/ discounts) + SubTotalGross domain.Price + // SubTotalNet is the sum of all delivery net subtotals (without shipping/ discounts) + SubTotalNet domain.Price + // SubTotalGrossWithDiscounts is the sum of row gross prices reduced by the applied discounts + SubTotalGrossWithDiscounts domain.Price + // SubTotalNetWithDiscounts is the sum of row net prices reduced by the net value of the applied discounts + SubTotalNetWithDiscounts domain.Price + // TotalDiscountAmount is the sum of all discounts (incl. shipping) + TotalDiscountAmount domain.Price + // NonItemRelatedDiscountAmount is the sum of discounts that are not related to the item (including shipping discounts) + NonItemRelatedDiscountAmount domain.Price + // ItemRelatedDiscountAmount is the sum of discounts that are related to the item (including shipping discounts) + ItemRelatedDiscountAmount domain.Price } // Teaser represents some teaser infos for cart @@ -127,14 +156,6 @@ type ( ReservedOrderID string } - // Builder is the main builder for a cart - Builder struct { - cartInBuilding *Cart - } - - // BuilderProvider should be used to create the cart by using the Builder - BuilderProvider func() *Builder - // PricedItems - value object that contains items of the different possible types, that have a price PricedItems struct { cartItems map[string]domain.Price @@ -273,7 +294,7 @@ func (c Cart) GetByItemID(itemID string) (*Item, error) { // GetTotalQty for the product in the cart func (c Cart) GetTotalQty(marketPlaceCode string, variantCode string) int { - qty := int(0) + qty := 0 for _, delivery := range c.Deliveries { for _, currentItem := range delivery.Cartitems { if currentItem.MarketplaceCode == marketPlaceCode && currentItem.VariantMarketPlaceCode == variantCode { @@ -358,15 +379,10 @@ func (c Cart) GetVoucherSavings() domain.Price { return price } -// GrandTotal is the final sum (Valued price) that need to be paid: GrandTotal = SubTotal + TaxAmount - DiscountAmount + sum of Totalitems = (Sum of Items RowTotalWithDiscountInclTax) + sum of Totalitems -func (c Cart) GrandTotal() domain.Price { - return c.GetAllPaymentRequiredItems().Sum() -} - // GetAllPaymentRequiredItems returns all the Items (Cartitem, ShippingItem, TotalItems) that need to be paid with the final gross price func (c Cart) GetAllPaymentRequiredItems() PricedItems { pricedItems := PricedItems{ - cartItems: make(map[string]domain.Price), + cartItems: make(map[string]domain.Price, c.ProductCount()), shippingItems: make(map[string]domain.Price, len(c.Deliveries)), totalItems: make(map[string]domain.Price, len(c.Totalitems)), } @@ -374,72 +390,19 @@ func (c Cart) GetAllPaymentRequiredItems() PricedItems { pricedItems.totalItems[ti.Code] = ti.Price } for _, del := range c.Deliveries { - if !del.ShippingItem.TotalWithDiscountInclTax().IsZero() { - pricedItems.shippingItems[del.DeliveryInfo.Code] = del.ShippingItem.TotalWithDiscountInclTax() + if !del.ShippingItem.PriceGrossWithDiscounts.IsZero() { + pricedItems.shippingItems[del.DeliveryInfo.Code] = del.ShippingItem.PriceGrossWithDiscounts } for _, ci := range del.Cartitems { - pricedItems.cartItems[ci.ID] = ci.RowPriceGrossWithDiscount() + pricedItems.cartItems[ci.ID] = ci.RowPriceGrossWithDiscount } } return pricedItems } -// SumShippingNet returns net sum price of deliveries ShippingItems -func (c Cart) SumShippingNet() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.ShippingItem.PriceNet) - } - - price, _ := domain.SumAll(prices...) - - return price -} - -// SumShippingNetWithDiscounts returns net sum price of deliveries ShippingItems with discounts -func (c Cart) SumShippingNetWithDiscounts() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - discount, _ := del.ShippingItem.AppliedDiscounts.Sum() - prices = append(prices, del.ShippingItem.PriceNet.ForceAdd(discount)) - } - - price, _ := domain.SumAll(prices...) - - return price -} - -// SumShippingGross returns gross sum price of deliveries ShippingItems -func (c Cart) SumShippingGross() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.ShippingItem.PriceGross) - } - - price, _ := domain.SumAll(prices...) - - return price -} - -// SumShippingGrossWithDiscounts returns gross sum price of deliveries ShippingItems with discounts -func (c Cart) SumShippingGrossWithDiscounts() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.ShippingItem.TotalWithDiscountInclTax()) - } - - price, _ := domain.SumAll(prices...) - - return price -} - // HasShippingCosts returns true if cart HasShippingCosts func (c Cart) HasShippingCosts() bool { - return c.SumShippingNet().IsPositive() + return c.ShippingNet.IsPositive() } // AllShippingTitles returns all ShippingItem titles @@ -453,19 +416,6 @@ func (c Cart) AllShippingTitles() []string { return label } -// SubTotalGross returns sum price of deliveries SubTotalGross -func (c Cart) SubTotalGross() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.SubTotalGross()) - } - - price, _ := domain.SumAll(prices...) - - return price -} - // SumTaxes returns sum of deliveries SumRowTaxes func (c Cart) SumTaxes() Taxes { newTaxes := Taxes{} @@ -485,83 +435,6 @@ func (c Cart) SumTotalTaxAmount() domain.Price { return c.SumTaxes().TotalAmount() } -// SubTotalNet returns sum price of deliveries SubTotalNet -func (c Cart) SubTotalNet() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.SubTotalNet()) - } - - price, _ := domain.SumAll(prices...) - - return price -} - -// SubTotalGrossWithDiscounts returns sum price of deliveries SubTotalGrossWithDiscounts -func (c Cart) SubTotalGrossWithDiscounts() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.SubTotalGrossWithDiscounts()) - } - - price, _ := domain.SumAll(prices...) - - return price -} - -// SubTotalNetWithDiscounts returns sum price of deliveries SubTotalNetWithDiscounts -func (c Cart) SubTotalNetWithDiscounts() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.SubTotalNetWithDiscounts()) - } - price, _ := domain.SumAll(prices...) - - return price -} - -// SumTotalDiscountAmount returns sum price of deliveries SumTotalDiscountAmount -func (c Cart) SumTotalDiscountAmount() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.SumTotalDiscountAmount()) - } - - price, _ := domain.SumAll(prices...) - - return price -} - -// SumNonItemRelatedDiscountAmount returns sum price of deliveries SumNonItemRelatedDiscountAmount -func (c Cart) SumNonItemRelatedDiscountAmount() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.SumNonItemRelatedDiscountAmount()) - } - - price, _ := domain.SumAll(prices...) - - return price -} - -// SumItemRelatedDiscountAmount returns sum price of deliveries SumItemRelatedDiscountAmount -func (c Cart) SumItemRelatedDiscountAmount() domain.Price { - prices := make([]domain.Price, 0, len(c.Deliveries)) - - for _, del := range c.Deliveries { - prices = append(prices, del.SumItemRelatedDiscountAmount()) - } - - price, _ := domain.SumAll(prices...) - - return price -} - // HasAppliedCouponCode checks if a coupon code is applied to the cart func (c Cart) HasAppliedCouponCode() bool { return len(c.AppliedCouponCodes) > 0 @@ -576,7 +449,7 @@ func (c Cart) GetCartTeaser() *Teaser { } } -// GetPaymentReference returns a string that can be used as reference to pass to payment gateway. You may want to use it. It returns either the reserved Order id or the cart id/entityid +// GetPaymentReference returns a string that can be used as reference to pass to payment gateway. You may want to use it. It returns either the reserved Order id or the cart id/entityID func (c Cart) GetPaymentReference() string { if c.AdditionalData.ReservedOrderID != "" { return c.AdditionalData.ReservedOrderID @@ -613,8 +486,8 @@ func (c Cart) GrandTotalCharges() domain.Charges { // else return the grand total as main charge charges := domain.Charges{} mainCharge := domain.Charge{ - Value: c.GrandTotal(), - Price: c.GrandTotal(), + Value: c.GrandTotal, + Price: c.GrandTotal, Type: domain.ChargeTypeMain, } @@ -625,7 +498,7 @@ func (c Cart) GrandTotalCharges() domain.Charges { // AddTax returns new Tax with this Tax added func (t Taxes) AddTax(tax Tax) Taxes { - newTaxes := Taxes(t) + newTaxes := t newTaxes = append(newTaxes, tax) return newTaxes @@ -633,7 +506,7 @@ func (t Taxes) AddTax(tax Tax) Taxes { // AddTaxWithMerge returns new Taxes with this Tax added func (t Taxes) AddTaxWithMerge(taxToAddOrMerge Tax) Taxes { - newTaxes := Taxes(t) + newTaxes := t for k, tax := range newTaxes { if tax.Type == taxToAddOrMerge.Type { @@ -654,7 +527,7 @@ func (t Taxes) AddTaxWithMerge(taxToAddOrMerge Tax) Taxes { // AddTaxesWithMerge returns new Taxes with the given Taxes all added or merged in func (t Taxes) AddTaxesWithMerge(taxes Taxes) Taxes { - newTaxes := Taxes(t) + newTaxes := t for _, tax := range taxes { newTaxes = newTaxes.AddTaxWithMerge(tax) @@ -676,131 +549,6 @@ func (t Taxes) TotalAmount() domain.Price { return result } -// Build is the main factory method -func (b *Builder) Build() (*Cart, error) { - return b.reset(nil) -} - -// SetIds sets the cart ids -func (b *Builder) SetIds(id string, entityID string) *Builder { - b.init() - b.cartInBuilding.ID = id - b.cartInBuilding.EntityID = entityID - - return b -} - -// SetReservedOrderID sets the optional order id -func (b *Builder) SetReservedOrderID(id string) *Builder { - b.init() - b.cartInBuilding.AdditionalData.ReservedOrderID = id - - return b -} - -// SetBillingAddress sets the optional billing address -func (b *Builder) SetBillingAddress(a Address) *Builder { - b.init() - b.cartInBuilding.BillingAddress = &a - - return b -} - -// SetPurchaser sets the optional purchaser -func (b *Builder) SetPurchaser(p Person) *Builder { - b.init() - b.cartInBuilding.Purchaser = &p - - return b -} - -// AddDelivery adds a delivery - use the DeliveryBuilder to create one -// todo Make sure that item id is unique over the whole cart (that is an invariant so we need to ensure that invalid cart objects cannot be build) -func (b *Builder) AddDelivery(d Delivery) *Builder { - b.init() - b.cartInBuilding.Deliveries = append(b.cartInBuilding.Deliveries, d) - - return b -} - -// SetAdditionalData can be used to add additional data -func (b *Builder) SetAdditionalData(d AdditionalData) *Builder { - b.init() - b.cartInBuilding.AdditionalData = d - - return b -} - -// SetPaymentSelection sets the payment selection -func (b *Builder) SetPaymentSelection(d PaymentSelection) *Builder { - b.init() - b.cartInBuilding.PaymentSelection = d - - return b -} - -// SetAuthenticatedUserID used to mark the cart as authenticated users cart -func (b *Builder) SetAuthenticatedUserID(id string) *Builder { - b.init() - b.cartInBuilding.AuthenticatedUserID = id - b.cartInBuilding.BelongsToAuthenticatedUser = true - - return b -} - -// SetBelongsToAuthenticatedUser mark the cart as authenticated users cart -func (b *Builder) SetBelongsToAuthenticatedUser(v bool) *Builder { - b.init() - b.cartInBuilding.BelongsToAuthenticatedUser = v - - return b -} - -// AddAppliedCouponCode add the optional coupon that is applied for the cart -func (b *Builder) AddAppliedCouponCode(code CouponCode) *Builder { - b.init() - b.cartInBuilding.AppliedCouponCodes = append(b.cartInBuilding.AppliedCouponCodes, code) - - return b -} - -// SetAppliedGiftCards sets the optional applied gift cards -func (b *Builder) SetAppliedGiftCards(gc []AppliedGiftCard) *Builder { - b.init() - b.cartInBuilding.AppliedGiftCards = gc - - return b -} - -// SetDefaultCurrency sets the default currency -func (b *Builder) SetDefaultCurrency(d string) *Builder { - b.init() - b.cartInBuilding.DefaultCurrency = d - - return b -} - -// AddTotalitem adds nontaxable extra totals on cart level -func (b *Builder) AddTotalitem(totali Totalitem) *Builder { - b.init() - b.cartInBuilding.Totalitems = append(b.cartInBuilding.Totalitems, totali) - - return b -} - -func (b *Builder) init() { - if b.cartInBuilding == nil { - b.cartInBuilding = &Cart{} - } -} - -func (b *Builder) reset(err error) (*Cart, error) { - cart := b.cartInBuilding - b.cartInBuilding = nil - - return cart, err -} - // Sum returns Sum of all items in this struct func (p PricedItems) Sum() domain.Price { prices := make([]domain.Price, 0, len(p.cartItems)+len(p.shippingItems)+len(p.totalItems)) diff --git a/cart/domain/cart/cart_test.go b/cart/domain/cart/cart_test.go index 9af86efc7..1860d42c1 100644 --- a/cart/domain/cart/cart_test.go +++ b/cart/domain/cart/cart_test.go @@ -7,7 +7,6 @@ import ( "testing" "flamingo.me/flamingo-commerce/v3/cart/domain/placeorder" - "flamingo.me/flamingo-commerce/v3/cart/domain/testutils" "github.com/stretchr/testify/require" "github.com/stretchr/testify/assert" @@ -451,131 +450,6 @@ func TestCart_GetVoucherSavings(t *testing.T) { assert.Equal(t, got, domain.Price{}) } -func TestCart_SumShippingNetWithDiscounts(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - cart cartDomain.Cart - want domain.Price - }{ - { - name: "empty cart", - cart: cartDomain.Cart{}, - want: domain.NewZero(""), - }, - { - name: "cart with items with discounts but no shipping cost", - cart: func() cartDomain.Cart { - cart := &cartDomain.Cart{} - cart.Deliveries = append(cart.Deliveries, *testutils.BuildDeliveryWithDifferentDiscounts(t)) - return *cart - }(), - want: domain.NewZero(""), - }, - { - name: "cart with items and shipping cost, both with discounts", - cart: func() cartDomain.Cart { - cart := &cartDomain.Cart{} - cart.Deliveries = append(cart.Deliveries, *testutils.BuildDeliveryWithDifferentDiscountsAndShippingDiscounts(t)) - return *cart - }(), - want: domain.NewFromFloat(5.0, "$"), - }, - { - name: "cart with multiple deliveries with items and shipping cost, some with discounts", - cart: func() cartDomain.Cart { - cart := &cartDomain.Cart{} - cart.Deliveries = append(cart.Deliveries, *testutils.BuildDeliveryWithDifferentDiscountsAndShippingDiscounts(t)) - cart.Deliveries = append(cart.Deliveries, *testutils.BuildDeliveryWithoutDiscountsAndShippingDiscounts(t)) - return *cart - }(), - want: domain.NewFromFloat(10.0, "$"), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.cart.SumShippingNetWithDiscounts(); !got.Equal(tt.want) { - t.Errorf("Cart.SumShippingNetWithDiscount() = %v, want %v", got.Amount(), tt.want.Amount()) - } - }) - } -} - -func TestCart_SumShippingGrossWithDiscounts(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - cart cartDomain.Cart - want domain.Price - }{ - { - name: "empty cart", - cart: cartDomain.Cart{}, - want: domain.NewZero(""), - }, - { - name: "cart with items with discounts but no shipping cost", - cart: func() cartDomain.Cart { - cart := &cartDomain.Cart{} - cart.Deliveries = append(cart.Deliveries, *testutils.BuildDeliveryWithDifferentDiscounts(t)) - return *cart - }(), - want: domain.NewZero(""), - }, - { - name: "cart with items and shipping cost, both with discounts", - cart: func() cartDomain.Cart { - cart := &cartDomain.Cart{} - cart.Deliveries = append(cart.Deliveries, *testutils.BuildDeliveryWithDifferentDiscountsAndShippingDiscounts(t)) - return *cart - }(), - want: domain.NewFromFloat(7.0, "$"), - }, - { - name: "cart with multiple deliveries with items and shipping cost, some with discounts", - cart: func() cartDomain.Cart { - cart := &cartDomain.Cart{} - cart.Deliveries = append(cart.Deliveries, *testutils.BuildDeliveryWithDifferentDiscountsAndShippingDiscounts(t)) - cart.Deliveries = append(cart.Deliveries, *testutils.BuildDeliveryWithoutDiscountsAndShippingDiscounts(t)) - return *cart - }(), - want: domain.NewFromFloat(14.0, "$"), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.cart.SumShippingGrossWithDiscounts(); !got.Equal(tt.want) { - t.Errorf("Cart.SumShippingGrossWithDiscounts() = %v, want %v", got.Amount(), tt.want.Amount()) - } - }) - } -} - -/* -func TestCart_HasNoMixedCart(t *testing.T) { - var cart = new(Cart) - - cart.Cartitems = append(cart.Cartitems, getItemWithDepartureIntent()) - - resultNoMixedCart := cart.HasMixedCart() - assert.False(t, resultNoMixedCart) -} - -func TestCart_HasMixedCart(t *testing.T) { - var cart = new(Cart) - - cart.Cartitems = append(cart.Cartitems, getItemWithDepartureIntent()) - cart.Cartitems = append(cart.Cartitems, getItemWithArrivalIntent()) - - resultMixedCart := cart.HasMixedCart() - assert.True(t, resultMixedCart) -} -*/ - func TestPlacedOrderInfos_GetOrderNumberForDeliveryCode(t *testing.T) { t.Parallel() @@ -680,20 +554,6 @@ func TestTaxes_AddTaxWithMerge(t *testing.T) { assert.Equal(t, 1, len(taxes)) } -func TestCartBuilder_BuildAndGet(t *testing.T) { - t.Parallel() - - b := cartDomain.Builder{} - - cart, err := b.AddTotalitem(cartDomain.Totalitem{ - Title: "test", - Price: domain.NewFromInt(100, 100, "EUR"), - }).SetIds("id", "").Build() - assert.NoError(t, err) - assert.Equal(t, domain.NewFromInt(100, 100, "EUR"), cart.GrandTotal(), "gradtotal need to match given total") - -} - func TestAppliedCouponCodes_ContainedIn(t *testing.T) { type args struct { couponCodesToCompare cartDomain.AppliedCouponCodes @@ -907,3 +767,221 @@ func TestCart_ProductCountAndUniqueProductCount(t *testing.T) { assert.Equal(t, tt.wantProductUniqueCount, tt.cart.ProductCountUnique(), "ProductCountUnique has wrong result, expected %#v but got %#v", tt.wantProductUniqueCount, tt.cart.ProductCountUnique()) } } + +func TestCart_GetAllPaymentRequiredItems(t *testing.T) { + cart := &cartDomain.Cart{ + Deliveries: []cartDomain.Delivery{ + { + DeliveryInfo: cartDomain.DeliveryInfo{Code: "delivery-1"}, + Cartitems: []cartDomain.Item{ + { + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(1234, 100, "$"), + }, + { + ID: "2", + RowPriceGrossWithDiscount: domain.NewFromInt(4321, 100, "$"), + }, + }, + ShippingItem: cartDomain.ShippingItem{ + PriceGrossWithDiscounts: domain.NewZero("$"), + }, + }, + { + DeliveryInfo: cartDomain.DeliveryInfo{Code: "delivery-2"}, + ShippingItem: cartDomain.ShippingItem{ + PriceGrossWithDiscounts: domain.NewFromInt(55, 10, "$"), + }, + }, + }, + Totalitems: []cartDomain.Totalitem{ + { + Code: "item-1", + Price: domain.NewFromInt(6789, 100, "$"), + }, + { + Code: "item-2", + Price: domain.NewFromInt(9876, 100, "$"), + }, + }, + } + + pricedItems := cart.GetAllPaymentRequiredItems() + assert.Len(t, pricedItems.CartItems(), cart.ProductCount()) + assert.Equal(t, 12.34, pricedItems.CartItems()["1"].FloatAmount()) + assert.Equal(t, 43.21, pricedItems.CartItems()["2"].FloatAmount()) + assert.Len(t, pricedItems.ShippingItems(), 1) + assert.Equal(t, 5.5, pricedItems.ShippingItems()["delivery-2"].FloatAmount()) + assert.Len(t, pricedItems.TotalItems(), len(cart.Totalitems)) + assert.Equal(t, 67.89, pricedItems.TotalItems()["item-1"].FloatAmount()) + assert.Equal(t, 98.76, pricedItems.TotalItems()["item-2"].FloatAmount()) +} + +func TestCart_GetContactMail(t *testing.T) { + tests := []struct { + name string + cart cartDomain.Cart + expectedEmail string + }{ + {name: "no mail", cart: cartDomain.Cart{}, expectedEmail: ""}, + {name: "billing email", cart: cartDomain.Cart{BillingAddress: &cartDomain.Address{Email: "foo@example.com"}}, expectedEmail: "foo@example.com"}, + {name: "shipping email", cart: cartDomain.Cart{Deliveries: []cartDomain.Delivery{{DeliveryInfo: cartDomain.DeliveryInfo{DeliveryLocation: cartDomain.DeliveryLocation{Address: &cartDomain.Address{Email: "foo@example.com"}}}}}}, expectedEmail: "foo@example.com"}, + {name: "billing and shipping email", + cart: cartDomain.Cart{ + BillingAddress: &cartDomain.Address{Email: "billing@example.com"}, + Deliveries: []cartDomain.Delivery{{DeliveryInfo: cartDomain.DeliveryInfo{DeliveryLocation: cartDomain.DeliveryLocation{Address: &cartDomain.Address{Email: "shipping@example.com"}}}}}, + }, + expectedEmail: "shipping@example.com", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expectedEmail, tt.cart.GetContactMail()) + }) + } +} + +func TestCart_GetDeliveryByCodeWithoutBool(t *testing.T) { + cart := cartDomain.Cart{Deliveries: []cartDomain.Delivery{{DeliveryInfo: cartDomain.DeliveryInfo{Code: "valid"}}}} + assert.Nil(t, cart.GetDeliveryByCodeWithoutBool("invalid")) + assert.NotNil(t, cart.GetDeliveryByCodeWithoutBool("valid")) +} + +func TestCart_GetByItemID(t *testing.T) { + cart := cartDomain.Cart{Deliveries: []cartDomain.Delivery{ + { + Cartitems: []cartDomain.Item{{ID: "item-1"}}, + }, + { + Cartitems: []cartDomain.Item{{ID: "item-2"}}, + }, + }} + + t.Run("invalid item id should return error", func(t *testing.T) { + got, err := cart.GetByItemID("invalid") + assert.Error(t, err) + assert.Nil(t, got) + }) + + t.Run("valid item id should return item", func(t *testing.T) { + got, err := cart.GetByItemID("item-2") + assert.NoError(t, err) + assert.NotNil(t, got) + assert.Equal(t, "item-2", got.ID) + }) +} + +func TestCart_IsPaymentSelected(t *testing.T) { + cart := cartDomain.Cart{} + assert.False(t, cart.IsPaymentSelected()) + cart.PaymentSelection = cartDomain.DefaultPaymentSelection{} + assert.True(t, cart.IsPaymentSelected()) +} + +func TestCart_HasShippingCosts(t *testing.T) { + cart := cartDomain.Cart{} + assert.False(t, cart.HasShippingCosts()) + + cart.ShippingNet = domain.NewFromFloat(5.00, "USD") + assert.True(t, cart.HasShippingCosts()) +} + +func TestCart_AllShippingTitles(t *testing.T) { + cart := cartDomain.Cart{} + assert.Equal(t, []string{}, cart.AllShippingTitles()) + + cart.Deliveries = []cartDomain.Delivery{ + {ShippingItem: cartDomain.ShippingItem{Title: "shipping a"}}, + {ShippingItem: cartDomain.ShippingItem{Title: "shipping b"}}, + } + assert.ElementsMatch(t, []string{"shipping a", "shipping b"}, cart.AllShippingTitles()) +} + +func TestCart_SumTaxes(t *testing.T) { + cart := cartDomain.Cart{} + assert.Len(t, cart.SumTaxes(), 0) + + cart.Deliveries = []cartDomain.Delivery{ + { + Cartitems: []cartDomain.Item{ + {RowTaxes: []cartDomain.Tax{{Type: "gst", Amount: domain.NewFromFloat(1.00, "USD")}}}, + {RowTaxes: []cartDomain.Tax{{Type: "foo", Amount: domain.NewFromFloat(2.00, "USD")}}}, + }, + ShippingItem: cartDomain.ShippingItem{TaxAmount: domain.NewFromFloat(2.00, "USD")}, + }, + { + Cartitems: []cartDomain.Item{ + {RowTaxes: []cartDomain.Tax{{Type: "gst", Amount: domain.NewFromFloat(1.00, "USD")}}}, + {RowTaxes: []cartDomain.Tax{{Type: "bar", Amount: domain.NewFromFloat(2.00, "USD")}}}, + }, + }, + } + + assert.Len(t, cart.SumTaxes(), 4) +} + +func TestCart_SumTotalTaxAmount(t *testing.T) { + cart := cartDomain.Cart{} + assert.Equal(t, cart.SumTotalTaxAmount(), domain.NewZero("")) + + cart.Deliveries = []cartDomain.Delivery{ + { + Cartitems: []cartDomain.Item{ + {RowTaxes: []cartDomain.Tax{{Type: "gst", Amount: domain.NewFromFloat(1.00, "USD")}}}, + {RowTaxes: []cartDomain.Tax{{Type: "foo", Amount: domain.NewFromFloat(2.00, "USD")}}}, + }, + ShippingItem: cartDomain.ShippingItem{TaxAmount: domain.NewFromFloat(2.00, "USD")}, + }, + { + Cartitems: []cartDomain.Item{ + {RowTaxes: []cartDomain.Tax{{Type: "gst", Amount: domain.NewFromFloat(1.00, "USD")}}}, + {RowTaxes: []cartDomain.Tax{{Type: "bar", Amount: domain.NewFromFloat(2.00, "USD")}}}, + }, + }, + } + + assert.Equal(t, cart.SumTotalTaxAmount(), domain.NewFromFloat(8.00, "USD")) +} + +func TestCart_HasAppliedCouponCode(t *testing.T) { + cart := cartDomain.Cart{} + assert.False(t, cart.HasAppliedCouponCode()) + + cart.AppliedCouponCodes = []cartDomain.CouponCode{{Code: "summer-2020"}} + assert.True(t, cart.HasAppliedCouponCode()) +} + +func TestCart_GetPaymentReference(t *testing.T) { + cart := cartDomain.Cart{EntityID: "e", ID: "i"} + assert.Equal(t, "i-e", cart.GetPaymentReference()) + + cart.AdditionalData.ReservedOrderID = "order-id" + assert.Equal(t, "order-id", cart.GetPaymentReference()) +} + +func TestCart_GrandTotalCharges(t *testing.T) { + cart := cartDomain.Cart{GrandTotal: domain.NewFromFloat(100.00, "USD")} + expected := domain.NewCharges(map[string]domain.Charge{ + domain.ChargeTypeMain: { + Type: domain.ChargeTypeMain, + Value: domain.NewFromFloat(100.00, "USD"), + Price: domain.NewFromFloat(100.00, "USD"), + }, + }) + + assert.Equal(t, *expected, cart.GrandTotalCharges()) +} + +func TestCart_GetTotalItemsByType(t *testing.T) { + cart := cartDomain.Cart{Totalitems: []cartDomain.Totalitem{ + {Type: "valid", Price: domain.NewFromFloat(5.00, "USD")}, + {Type: "valid", Price: domain.NewFromFloat(2.00, "USD")}, + {Type: "other", Price: domain.NewFromFloat(5.00, "USD")}, + }} + assert.Equal(t, []cartDomain.Totalitem{}, cart.GetTotalItemsByType("invalid")) + assert.Equal(t, []cartDomain.Totalitem{ + {Type: "valid", Price: domain.NewFromFloat(5.00, "USD")}, + {Type: "valid", Price: domain.NewFromFloat(2.00, "USD")}, + }, cart.GetTotalItemsByType("valid")) +} diff --git a/cart/domain/cart/delivery.go b/cart/domain/cart/delivery.go index 51981b3d2..00ea6c302 100644 --- a/cart/domain/cart/delivery.go +++ b/cart/domain/cart/delivery.go @@ -2,7 +2,6 @@ package cart import ( "encoding/json" - "errors" "time" priceDomain "flamingo.me/flamingo-commerce/v3/price/domain" @@ -17,6 +16,25 @@ type ( Cartitems []Item // ShippingItem represent the shipping cost that may be involved in this delivery ShippingItem ShippingItem + + // SubTotalGross contains the sum of row gross prices, without shipping/discounts + SubTotalGross priceDomain.Price + // SubTotalNet contains the sum of row net prices, without shipping/discounts + SubTotalNet priceDomain.Price + // TotalDiscountAmount contains the sum of all discounts (incl. shipping) + TotalDiscountAmount priceDomain.Price + // TotalDiscountAmount contains the sum of all discounts (excl. shipping) + SubTotalDiscountAmount priceDomain.Price + // NonItemRelatedDiscountAmount contains the sum of discounts that are not related to the item, e.g. a general promo + NonItemRelatedDiscountAmount priceDomain.Price + // ItemRelatedDiscountAmount contains the sum of discounts that are related to the item, e.g. promo due to product attribute + ItemRelatedDiscountAmount priceDomain.Price + // SubTotalGrossWithDiscounts contains the sum of row gross prices reduced by the applied discounts + SubTotalGrossWithDiscounts priceDomain.Price + // SubTotalNetWithDiscounts contains the sum of row net prices reduced by the net value of the applied discounts + SubTotalNetWithDiscounts priceDomain.Price + // GrandTotal contains the final price to pay + GrandTotal priceDomain.Price } // DeliveryInfo - represents the Delivery @@ -40,13 +58,15 @@ type ( AdditionalDeliveryInfos map[string]json.RawMessage `swaggerignore:"true"` } - // ShippingItem value object + // ShippingItem represents shipping costs that need to be paid by the customer ShippingItem struct { - Title string - PriceNet priceDomain.Price - PriceGross priceDomain.Price - TaxAmount priceDomain.Price - AppliedDiscounts AppliedDiscounts + Title string + PriceNet priceDomain.Price + PriceNetWithDiscounts priceDomain.Price + PriceGross priceDomain.Price + PriceGrossWithDiscounts priceDomain.Price + TaxAmount priceDomain.Price + AppliedDiscounts AppliedDiscounts } // AdditionalDeliverInfo is an interface that allows to store "any" additional objects on the cart @@ -56,7 +76,7 @@ type ( Unmarshal(json.RawMessage) error } - // DeliveryLocation value object + // DeliveryLocation hold information about where the items should be delivered DeliveryLocation struct { // Type is the type of the delivery - use some of the constant defined in the package like DeliverylocationTypeAddress Type string @@ -67,14 +87,6 @@ type ( // Code is an optional identifier of this location/destination Code string } - - // DeliveryBuilder is the Builder (factory) to build new deliveries by making sure the invariants are ok - DeliveryBuilder struct { - deliveryInBuilding *Delivery - } - - // DeliveryBuilderProvider should be used to create a Delivery - DeliveryBuilderProvider func() *DeliveryBuilder ) const ( @@ -97,36 +109,6 @@ const ( DeliverylocationTypeFreightstation = "freight-station" ) -// LoadAdditionalInfo returns the additional Data -func (di *DeliveryInfo) LoadAdditionalInfo(key string, info AdditionalDeliverInfo) error { - - if di.AdditionalDeliveryInfos == nil { - return ErrAdditionalInfosNotFound - } - if val, ok := di.AdditionalDeliveryInfos[key]; ok { - return info.Unmarshal(val) - } - return ErrAdditionalInfosNotFound -} - -// SubTotalGross returns sub total of all items including the taxes for the delivery -func (d Delivery) SubTotalGross() priceDomain.Price { - prices := make([]priceDomain.Price, 0, len(d.Cartitems)) - - for _, item := range d.Cartitems { - prices = append(prices, item.RowPriceGross) - } - result, _ := priceDomain.SumAll(prices...) - - return result -} - -// GrandTotal returns the sub total of all items, shipping costs and potential discounts including taxes for the delivery -func (d Delivery) GrandTotal() priceDomain.Price { - result, _ := priceDomain.SumAll(d.SubTotalGross(), d.ShippingItem.PriceGross, d.SumTotalDiscountAmount()) - return result -} - // SumRowTaxes returns all taxes applied to items of this delivery func (d Delivery) SumRowTaxes() Taxes { var taxes Taxes @@ -152,162 +134,11 @@ func (d Delivery) SumTotalTaxAmount() priceDomain.Price { return result } -// SubTotalNet returns sub total of all items excluding the taxes for the delivery -func (d Delivery) SubTotalNet() priceDomain.Price { - prices := make([]priceDomain.Price, 0, len(d.Cartitems)) - - for _, item := range d.Cartitems { - prices = append(prices, item.RowPriceNet) - } - result, _ := priceDomain.SumAll(prices...) - - return result -} - -// SumTotalDiscountAmount returns the total applied discounts of the items of the delivery -func (d Delivery) SumTotalDiscountAmount() priceDomain.Price { - result := d.SumSubTotalDiscountAmount() - - shippingDiscount, _ := d.ShippingItem.AppliedDiscounts.Sum() - result, _ = result.Add(shippingDiscount) - - return result -} - -// SumSubTotalDiscountAmount returns the total applied discounts of the items of the delivery -func (d Delivery) SumSubTotalDiscountAmount() priceDomain.Price { - prices := make([]priceDomain.Price, 0, len(d.Cartitems)) - - for _, item := range d.Cartitems { - prices = append(prices, item.TotalDiscountAmount()) - } - result, _ := priceDomain.SumAll(prices...) - - return result -} - -// SumNonItemRelatedDiscountAmount returns the total applied discounts that are not item related -func (d Delivery) SumNonItemRelatedDiscountAmount() priceDomain.Price { - prices := make([]priceDomain.Price, 0, len(d.Cartitems)+len(d.ShippingItem.AppliedDiscounts)) - - for _, discount := range d.ShippingItem.AppliedDiscounts { - if !discount.IsItemRelated { - prices = append(prices, discount.Applied) - } - } - - for _, item := range d.Cartitems { - prices = append(prices, item.NonItemRelatedDiscountAmount()) - } - result, _ := priceDomain.SumAll(prices...) - - return result -} - -// SumItemRelatedDiscountAmount returns the total applied discounts that are item related -func (d Delivery) SumItemRelatedDiscountAmount() priceDomain.Price { - prices := make([]priceDomain.Price, 0, len(d.Cartitems)+len(d.ShippingItem.AppliedDiscounts)) - - for _, discount := range d.ShippingItem.AppliedDiscounts { - if discount.IsItemRelated { - prices = append(prices, discount.Applied) - } - } - - for _, item := range d.Cartitems { - prices = append(prices, item.ItemRelatedDiscountAmount()) - } - result, _ := priceDomain.SumAll(prices...) - - return result -} - -// SubTotalGrossWithDiscounts returns sub total of all items including the taxes and applied discounts for the delivery -func (d Delivery) SubTotalGrossWithDiscounts() priceDomain.Price { - price, _ := d.SubTotalGross().Add(d.SumSubTotalDiscountAmount()) - - return price -} - -// SubTotalNetWithDiscounts returns sub total of all items excluding the taxes and applied discounts for the delivery -func (d Delivery) SubTotalNetWithDiscounts() priceDomain.Price { - price, _ := d.SubTotalNet().Add(d.SumSubTotalDiscountAmount()) - - return price -} - // HasItems returns true if there are items under the delivery func (d Delivery) HasItems() bool { return len(d.Cartitems) > 0 } -// Copy can be used to set the values for the new delivery from an existing delivery by copying it -func (f *DeliveryBuilder) Copy(d *Delivery) *DeliveryBuilder { - f.init() - f.deliveryInBuilding.Cartitems = d.Cartitems - f.deliveryInBuilding.ShippingItem = d.ShippingItem - f.deliveryInBuilding.DeliveryInfo = d.DeliveryInfo - - return f -} - -// AddItem adds an item to the delivery -func (f *DeliveryBuilder) AddItem(i Item) *DeliveryBuilder { - f.init() - f.deliveryInBuilding.Cartitems = append(f.deliveryInBuilding.Cartitems, i) - return f -} - -// SetShippingItem sets the delivery ShippingItem -func (f *DeliveryBuilder) SetShippingItem(i ShippingItem) *DeliveryBuilder { - f.init() - f.deliveryInBuilding.ShippingItem = i - return f -} - -// SetDeliveryInfo sets DeliveryInfo -func (f *DeliveryBuilder) SetDeliveryInfo(i DeliveryInfo) *DeliveryBuilder { - f.init() - f.deliveryInBuilding.DeliveryInfo = i - return f -} - -// SetDeliveryCode sets the deliveryCode (dont need to be called if SetDeliveryInfo has a code set already) -func (f *DeliveryBuilder) SetDeliveryCode(code string) *DeliveryBuilder { - f.init() - f.deliveryInBuilding.DeliveryInfo.Code = code - return f -} - -// Build is the main Factory method -func (f *DeliveryBuilder) Build() (*Delivery, error) { - if f.deliveryInBuilding == nil { - return nil, errors.New("Nothing in building") - } - if f.deliveryInBuilding.DeliveryInfo.Code == "" { - return nil, errors.New("DeliveryInfo.Code is not allowed empty") - } - - return f.deliveryInBuilding, nil -} - -func (f *DeliveryBuilder) init() { - if f.deliveryInBuilding == nil { - f.deliveryInBuilding = &Delivery{} - } -} - -func (f *DeliveryBuilder) reset() { - f.deliveryInBuilding = nil -} - -// TotalWithDiscountInclTax is the price the customer need to pay for the shipping -func (s ShippingItem) TotalWithDiscountInclTax() priceDomain.Price { - discounts, _ := s.AppliedDiscounts.Sum() - price, _ := s.PriceGross.Add(discounts) - return price.GetPayable() -} - // Tax is the Tax of the shipping func (s ShippingItem) Tax() Tax { return Tax{ @@ -316,6 +147,17 @@ func (s ShippingItem) Tax() Tax { } } +// LoadAdditionalInfo returns the additional Data +func (di *DeliveryInfo) LoadAdditionalInfo(key string, info AdditionalDeliverInfo) error { + if di.AdditionalDeliveryInfos == nil { + return ErrAdditionalInfosNotFound + } + if val, ok := di.AdditionalDeliveryInfos[key]; ok { + return info.Unmarshal(val) + } + return ErrAdditionalInfosNotFound +} + // GetAdditionalData returns additional data func (di DeliveryInfo) GetAdditionalData(key string) string { attribute := di.AdditionalData[key] diff --git a/cart/domain/cart/deliveryInfoBuilder.go b/cart/domain/cart/deliveryInfoBuilder.go index e931bb0f8..861be4158 100644 --- a/cart/domain/cart/deliveryInfoBuilder.go +++ b/cart/domain/cart/deliveryInfoBuilder.go @@ -8,10 +8,9 @@ import ( type ( - //DeliveryInfoBuilder - Factory + // DeliveryInfoBuilder can be used to set delivery infos depending on supplied delivery code DeliveryInfoBuilder interface { BuildByDeliveryCode(deliveryCode string) (*DeliveryInfo, error) - //BuildDeliveryInfoUpdateCommand(ctx web.Context, decoratedCart *DecoratedCart) ([]DeliveryInfoUpdateCommand, error) } // DefaultDeliveryInfoBuilder defines the default delivery info builder used diff --git a/cart/domain/cart/delivery_test.go b/cart/domain/cart/delivery_test.go index 892b4cd6f..45decf79b 100644 --- a/cart/domain/cart/delivery_test.go +++ b/cart/domain/cart/delivery_test.go @@ -1,118 +1,108 @@ -package cart +package cart_test import ( + "encoding/json" "fmt" "math/big" "testing" + "flamingo.me/flamingo-commerce/v3/cart/domain/cart" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" priceDomain "flamingo.me/flamingo-commerce/v3/price/domain" ) -func TestDeliveryInfo_TotalCalculations(t *testing.T) { - df := DeliveryBuilder{} - df.SetDeliveryCode("test") - - // Add item with 4,1 $ - 10 Qty and Tax of 7 - itemf := ItemBuilder{} - // Set net price 410 - itemf.SetSinglePriceNet( - priceDomain.NewFromInt(410, 100, "$"), - ).SetQty(10).AddTaxInfo( - "gst", new(big.Float).SetInt64(7), nil, - ).SetID("1") - - item, err := itemf.CalculatePricesAndTaxAmountsFromSinglePriceNet().Build() - if err != nil { - assert.FailNow(t, "no error excpected here", err) - } - if item == nil { - t.Fatal("item is nil but no error?") +func TestDelivery_TaxCalculations(t *testing.T) { + d := cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "foo"}, + Cartitems: []cart.Item{ + {ID: "1", RowTaxes: []cart.Tax{{Type: "gst", Rate: new(big.Float).SetInt64(7), Amount: priceDomain.NewFromInt(287, 100, "$")}}}, + {ID: "2", RowTaxes: []cart.Tax{{Type: "gst", Rate: new(big.Float).SetInt64(7), Amount: priceDomain.NewFromInt(175, 100, "$")}}}, + }, + ShippingItem: cart.ShippingItem{ + Title: "shipping", + TaxAmount: priceDomain.NewFromInt(55, 100, "$"), + }, } - assert.Equal(t, priceDomain.NewFromInt(4387, 100, "$"), item.RowPriceGross, "item1 gross price wrong") - df.AddItem(*item) - - // Add item with 10 $ - 5 Qty / Discount of 25 (505) and Tax of 7 - // Set net price 410 - itemf.SetSinglePriceNet( - priceDomain.NewFromInt(1000, 100, "$"), - ).SetQty(5).AddTaxInfo( - "gst", new(big.Float).SetInt64(7), nil, - ).SetID("1") - itemf.AddDiscount(AppliedDiscount{ - CampaignCode: "summercampaign", - Applied: priceDomain.NewFromInt(-2500, 100, "$"), - IsItemRelated: true, - }) - item2, err := itemf.CalculatePricesAndTaxAmountsFromSinglePriceNet().Build() - if err != nil { - assert.FailNow(t, "no error excpected here", err) - } - if item2 == nil { - t.Fatal("item2 is nil but no error?") - } - // item2 gros should be tax = (5 * 10) - discount (25) * 0.07 = 1,75 - assert.Equal( - t, - priceDomain.NewFromInt(5175, 100, "$").FloatAmount(), - item2.RowPriceGross.FloatAmount(), - "item2 gross price wrong", - ) - df.AddItem(*item2) - - df.SetShippingItem(ShippingItem{ - Title: "shipping", - PriceNet: priceDomain.NewFromInt(500, 100, "$"), - TaxAmount: priceDomain.NewFromInt(55, 100, "$"), - PriceGross: priceDomain.NewFromInt(555, 100, "$"), - AppliedDiscounts: AppliedDiscounts{ - { - Applied: priceDomain.NewFromInt(-20, 100, "$"), - }, - }, - }) + // check total tax amount + assert.True(t, priceDomain.NewFromInt(462+55, 100, "$").Equal(d.SumTotalTaxAmount()), fmt.Sprintf("result wrong %f", d.SumTotalTaxAmount().FloatAmount())) - // Check totals - d, err := df.Build() - assert.NoError(t, err) + // check row taxes + taxes := d.SumRowTaxes() + assert.Equal(t, 1, len(taxes), "expected gst to be merged") + assert.Equal(t, "gst", taxes[0].Type, "expected gst as type") + assert.Equal(t, new(big.Float).SetInt64(7), taxes[0].Rate, "expected rate to be correct") + assertPricesWithLikelyEqual(t, priceDomain.NewFromInt(462, 100, "$"), taxes.TotalAmount(), "total tax amount wrong") +} - // SubTotalGross - need to be 5175 + 4387 - assertPricesWithLikelyEqual(t, priceDomain.NewFromInt(9562, 100, "$"), d.SubTotalGross(), "SubTotalGross result should match 95,62") +// assertPricesWithLikelyEqual - helper +func assertPricesWithLikelyEqual(t *testing.T, p1 priceDomain.Price, p2 priceDomain.Price, msg string) { + t.Helper() + assert.True(t, p1.LikelyEqual(p2), fmt.Sprintf("%v (%f != %f)", msg, p1.FloatAmount(), p2.FloatAmount())) - // assert.Equal(t, priceDomain.NewFromInt(9562, 100, "$"), d.SubTotalGross(), fmt.Sprintf("SubTotalGross result should match 95,62 but is %f", d.SubTotalGross().FloatAmount())) +} - // SubTotalGross - need to be 4100 + 5000 - assert.True(t, priceDomain.NewFromInt(9100, 100, "$").Equal(d.SubTotalNet()), "SubTotalNet wrong") +func TestDelivery_HasItems(t *testing.T) { + delivery := cart.Delivery{Cartitems: []cart.Item{{}}} + assert.True(t, delivery.HasItems()) - // SumTotalTaxAmount is the difference - assert.True(t, priceDomain.NewFromInt(462+55, 100, "$").Equal(d.SumTotalTaxAmount()), fmt.Sprintf("result wrong %f", d.SumTotalTaxAmount().FloatAmount())) + delivery = cart.Delivery{} + assert.False(t, delivery.HasItems()) +} + +func TestShippingItem_Tax(t *testing.T) { + shippingItem := cart.ShippingItem{TaxAmount: priceDomain.NewFromInt(250, 100, "$")} + assert.Equal(t, "tax", shippingItem.Tax().Type) + assert.Equal(t, priceDomain.NewFromInt(250, 100, "$"), shippingItem.Tax().Amount) +} - // SumTotalDiscountAmount - assert.True(t, priceDomain.NewFromInt(-2500-20, 100, "$").Equal(d.SumTotalDiscountAmount()), fmt.Sprintf("SumTotalDiscountAmount result wrong %f", d.SumTotalDiscountAmount().FloatAmount())) +var _ cart.AdditionalDeliverInfo = &dummyDeliveryInfos{} - // SubTotalNetWithDiscounts - assert.True(t, priceDomain.NewFromInt(9100-2500, 100, "$").Equal(d.SubTotalNetWithDiscounts()), fmt.Sprintf("SubTotalNetWithDiscounts result wrong %f", d.SubTotalNetWithDiscounts().FloatAmount())) +type dummyDeliveryInfos struct { + Works bool +} - // SubTotalGrossWithDiscounts - assertPricesWithLikelyEqual(t, priceDomain.NewFromInt(9562-2500, 100, "$"), d.SubTotalGrossWithDiscounts(), "SubTotalGrossWithDiscount") +func (d *dummyDeliveryInfos) Marshal() (json.RawMessage, error) { + return json.Marshal(d) +} - assertPricesWithLikelyEqual(t, priceDomain.NewFromInt(-2500, 100, "$"), d.SumItemRelatedDiscountAmount(), "SumItemRelatedDiscountAmount") - assertPricesWithLikelyEqual(t, priceDomain.NewFromInt(-20, 100, "$"), d.SumNonItemRelatedDiscountAmount(), "SumNonItemRelatedDiscountAmount") - assertPricesWithLikelyEqual(t, priceDomain.NewFromInt(-2500-20, 100, "$"), d.SumTotalDiscountAmount(), "SumTotalDiscountAmount") +func (d *dummyDeliveryInfos) Unmarshal(message json.RawMessage) error { + return json.Unmarshal(message, d) +} - // Taxes check - taxes := d.SumRowTaxes() - assert.Equal(t, 1, len(taxes), "expected one merged tax") - assertPricesWithLikelyEqual(t, priceDomain.NewFromInt(462, 100, "$"), taxes.TotalAmount(), "taxes check wrong") +func TestDeliveryInfo_LoadAdditionalInfo(t *testing.T) { + t.Run("green line", func(t *testing.T) { + var info dummyDeliveryInfos + deliveryInfo := cart.DeliveryInfo{ + AdditionalDeliveryInfos: map[string]json.RawMessage{"foo": json.RawMessage(`{"Works":true}`)}} + require.NoError(t, deliveryInfo.LoadAdditionalInfo("foo", &info)) + assert.True(t, info.Works) + }) + t.Run("missing additional key should lead to error", func(t *testing.T) { + deliveryInfo := cart.DeliveryInfo{} + require.Error(t, deliveryInfo.LoadAdditionalInfo("missing", &dummyDeliveryInfos{})) - assertPricesWithLikelyEqual(t, priceDomain.NewFromInt(9562-2500+555-20, 100, "$"), d.GrandTotal(), "GrandTotal") + deliveryInfo = cart.DeliveryInfo{AdditionalDeliveryInfos: map[string]json.RawMessage{"foo": json.RawMessage(`{"Works":true}`)}} + require.Error(t, deliveryInfo.LoadAdditionalInfo("missing", &dummyDeliveryInfos{})) + }) } -// assertPricesWithLikelyEqual - helper -func assertPricesWithLikelyEqual(t *testing.T, p1 priceDomain.Price, p2 priceDomain.Price, msg string) { - t.Helper() - assert.True(t, p1.LikelyEqual(p2), fmt.Sprintf("%v (%f != %f)", msg, p1.FloatAmount(), p2.FloatAmount())) +func TestDeliveryInfo_GetAdditionalDeliveryInfo(t *testing.T) { + deliveryInfo := cart.DeliveryInfo{AdditionalDeliveryInfos: map[string]json.RawMessage{"foo": json.RawMessage("hello-world"), "bar": []byte{}}} + assert.Equal(t, json.RawMessage("hello-world"), deliveryInfo.GetAdditionalDeliveryInfo("foo")) +} +func TestDeliveryInfo_AdditionalDeliveryInfoKeys(t *testing.T) { + deliveryInfo := cart.DeliveryInfo{AdditionalDeliveryInfos: map[string]json.RawMessage{"foo": []byte{}, "bar": []byte{}}} + assert.ElementsMatch(t, []string{"foo", "bar"}, deliveryInfo.AdditionalDeliveryInfoKeys()) +} +func TestDeliveryInfo_GetAdditionalData(t *testing.T) { + deliveryInfo := cart.DeliveryInfo{AdditionalData: map[string]string{"foo": "bar"}} + assert.Equal(t, "bar", deliveryInfo.GetAdditionalData("foo")) +} +func TestDeliveryInfo_AdditionalDataKeys(t *testing.T) { + deliveryInfo := cart.DeliveryInfo{AdditionalData: map[string]string{"foo": "bar", "baz": "bar"}} + assert.ElementsMatch(t, []string{"foo", "baz"}, deliveryInfo.AdditionalDataKeys()) } diff --git a/cart/domain/cart/discount.go b/cart/domain/cart/discount.go index afca97daf..a8c25a3f1 100644 --- a/cart/domain/cart/discount.go +++ b/cart/domain/cart/discount.go @@ -102,6 +102,12 @@ func (i *Item) HasAppliedDiscounts() (bool, error) { return hasAppliedDiscounts(i) } +// TotalWithDiscountInclTax returns the final shipping price to pay +// Deprecated use public field PriceGrossWithDiscounts +func (s *ShippingItem) TotalWithDiscountInclTax() domain.Price { + return s.PriceGrossWithDiscounts +} + // MergeDiscounts parses discounts of a shipping item // All discounts with the same campaign code are aggregated and returned as one with a summed price func (s *ShippingItem) MergeDiscounts() (AppliedDiscounts, error) { diff --git a/cart/domain/cart/discount_test.go b/cart/domain/cart/discount_test.go index 58fc12d8e..c7b3e2b47 100644 --- a/cart/domain/cart/discount_test.go +++ b/cart/domain/cart/discount_test.go @@ -25,13 +25,10 @@ func TestCart_MergeDiscounts(t *testing.T) { cart: &cart.Cart{ Deliveries: func() []cart.Delivery { result := make([]cart.Delivery, 0) - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - delivery, _ := builder.Build() - result = append(result, *delivery) - builder = cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - result = append(result, *delivery) + delivery := cart.Delivery{DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}} + result = append(result, delivery) + delivery = cart.Delivery{DeliveryInfo: cart.DeliveryInfo{Code: "code-2"}} + result = append(result, delivery) return result }(), }, @@ -42,15 +39,15 @@ func TestCart_MergeDiscounts(t *testing.T) { cart: &cart.Cart{ Deliveries: func() []cart.Delivery { result := make([]cart.Delivery, 0) - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - builder.AddItem(cart.Item{}) - builder.AddItem(cart.Item{}) - delivery, _ := builder.Build() - result = append(result, *delivery) - builder = cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - result = append(result, *delivery) + delivery := cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}, + Cartitems: []cart.Item{{}, {}}, + } + result = append(result, delivery) + delivery = cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code-2"}, + } + result = append(result, delivery) return result }(), }, @@ -176,17 +173,19 @@ func TestCart_MergeDiscounts(t *testing.T) { cart: &cart.Cart{ Deliveries: func() []cart.Delivery { result := make([]cart.Delivery, 0) - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - builder.AddItem(cart.Item{}) - builder.AddItem(cart.Item{}) - builder.SetShippingItem(*testutils.BuildShippingItemWithDiscounts(t)) - delivery, _ := builder.Build() - result = append(result, *delivery) - builder = cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - builder.SetShippingItem(*testutils.BuildShippingItemWithDiscounts(t)) - result = append(result, *delivery) + + delivery := cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}, + Cartitems: []cart.Item{{}, {}}, + ShippingItem: *testutils.BuildShippingItemWithDiscounts(t), + } + result = append(result, delivery) + delivery = cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code-2"}, + Cartitems: []cart.Item{{}, {}}, + ShippingItem: *testutils.BuildShippingItemWithDiscounts(t), + } + result = append(result, delivery) return result }(), }, @@ -263,13 +262,10 @@ func TestCart_HasDiscounts(t *testing.T) { cart: &cart.Cart{ Deliveries: func() []cart.Delivery { result := make([]cart.Delivery, 0) - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - delivery, _ := builder.Build() - result = append(result, *delivery) - builder = cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - result = append(result, *delivery) + delivery := cart.Delivery{DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}} + result = append(result, delivery) + delivery = cart.Delivery{DeliveryInfo: cart.DeliveryInfo{Code: "code-2"}} + result = append(result, delivery) return result }(), }, @@ -280,15 +276,10 @@ func TestCart_HasDiscounts(t *testing.T) { cart: &cart.Cart{ Deliveries: func() []cart.Delivery { result := make([]cart.Delivery, 0) - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - builder.AddItem(cart.Item{}) - builder.AddItem(cart.Item{}) - delivery, _ := builder.Build() - result = append(result, *delivery) - builder = cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - result = append(result, *delivery) + delivery := cart.Delivery{DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}, Cartitems: []cart.Item{{}, {}}} + result = append(result, delivery) + delivery = cart.Delivery{DeliveryInfo: cart.DeliveryInfo{Code: "code-2"}, Cartitems: []cart.Item{{}, {}}} + result = append(result, delivery) return result }(), }, @@ -608,16 +599,11 @@ func TestItem_HasDiscounts(t *testing.T) { }, { name: "multiple discounts on item", - item: func() *cart.Item { - builder := cart.ItemBuilder{} - builder.AddDiscount(cart.AppliedDiscount{ - CampaignCode: "code-1", - Label: "title-1", - Type: "type-1", - }) - item, _ := builder.Build() - return item - }(), + item: &cart.Item{AppliedDiscounts: []cart.AppliedDiscount{cart.AppliedDiscount{ + CampaignCode: "code-1", + Label: "title-1", + Type: "type-1", + }}}, want: true, }, { diff --git a/cart/domain/cart/giftcard.go b/cart/domain/cart/giftcard.go index cfaa25a31..58503663b 100644 --- a/cart/domain/cart/giftcard.go +++ b/cart/domain/cart/giftcard.go @@ -20,8 +20,6 @@ type ( WithGiftCard interface { HasRemainingGiftCards() bool HasAppliedGiftCards() bool - SumAppliedGiftCards() (domain.Price, error) - SumGrandTotalWithGiftCards() (domain.Price, error) } ) @@ -50,45 +48,6 @@ func (c Cart) HasAppliedGiftCards() bool { return len(c.AppliedGiftCards) > 0 } -// SumAppliedGiftCards sum up all applied amounts of giftcads -// price is returned as a payable -func (c Cart) SumAppliedGiftCards() (domain.Price, error) { - // guard for no gift cards applied - if len(c.AppliedGiftCards) == 0 { - return domain.Price{}.GetPayable(), nil - } - prices := make([]domain.Price, 0, len(c.AppliedGiftCards)) - // add prices to array - for _, card := range c.AppliedGiftCards { - prices = append(prices, card.Applied) - } - price, err := domain.SumAll(prices...) - // in case of error regarding sum, pass on error - if err != nil { - return domain.Price{}.GetPayable(), err - } - return price.GetPayable(), nil -} - -// SumGrandTotalWithGiftCards calculate the grand total of the cart minus gift cards -func (c Cart) SumGrandTotalWithGiftCards() (domain.Price, error) { - giftCardTotal, err := c.SumAppliedGiftCards() - if err != nil { - return domain.Price{}.GetPayable(), err - } - // if there are no gift cards just return cart grand total - total := c.GrandTotal() - if giftCardTotal.IsZero() { - return total.GetPayable(), err - } - // subtract gift card total from total for "remaining total" - result, err := total.Sub(giftCardTotal) - if err != nil { - return domain.Price{}.GetPayable(), err - } - return result.GetPayable(), nil -} - // HasRemainingGiftCards check whether there are gift cards with remaining balance func (c Cart) HasRemainingGiftCards() bool { for _, card := range c.AppliedGiftCards { diff --git a/cart/domain/cart/giftcard_test.go b/cart/domain/cart/giftcard_test.go index 9b54f38ff..68fecf2ba 100644 --- a/cart/domain/cart/giftcard_test.go +++ b/cart/domain/cart/giftcard_test.go @@ -10,42 +10,6 @@ import ( "flamingo.me/flamingo-commerce/v3/price/domain" ) -func TestCart_SumAppliedGiftCards(t *testing.T) { - tests := []struct { - name string - cart *cart.Cart - want domain.Price - }{ - { - name: "empty cart", - cart: &cart.Cart{}, - want: domain.Price{}.GetPayable(), - }, - { - name: "cart with gift cards applied", - cart: &cart.Cart{ - AppliedGiftCards: cart.AppliedGiftCards{ - { - Applied: domain.NewFromFloat(15.0, "$"), - }, - { - Applied: domain.NewFromFloat(25.99, "$"), - }, - }, - }, - want: domain.NewFromFloat(40.99, "$").GetPayable(), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, _ := tt.cart.SumAppliedGiftCards() - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Cart.CanSumGiftCards() = %v, want %v", got, tt.want) - } - }) - } -} - func TestCart_HasAppliedGiftCards(t *testing.T) { tests := []struct { name string @@ -82,72 +46,6 @@ func TestCart_HasAppliedGiftCards(t *testing.T) { } } -func TestCart_SumGrandTotalWithGiftCards(t *testing.T) { - tests := []struct { - name string - cart *cart.Cart - want domain.Price - }{ - { - name: "empty cart", - cart: &cart.Cart{}, - want: domain.Price{}.GetPayable(), - }, - { - name: "cart without discounts", - cart: &cart.Cart{ - ID: "id-1", - Deliveries: []cart.Delivery{ - { - Cartitems: []cart.Item{ - { - ID: "test-1", - Qty: 1, - RowPriceGross: domain.NewFromFloat(10, "$").GetPayable(), - }, - }, - }, - }, - }, - want: domain.NewFromFloat(10.0, "$").GetPayable(), - }, - { - name: "cart with gift cards applied", - cart: &cart.Cart{ - ID: "id-1", - Deliveries: []cart.Delivery{ - { - Cartitems: []cart.Item{ - { - ID: "test-1", - Qty: 1, - RowPriceGross: domain.NewFromFloat(50.99, "$").GetPayable(), - }, - }, - }, - }, - AppliedGiftCards: cart.AppliedGiftCards{ - { - Applied: domain.NewFromFloat(15.0, "$").GetPayable(), - }, - { - Applied: domain.NewFromFloat(25.99, "$").GetPayable(), - }, - }, - }, - want: domain.NewFromFloat(10.0, "$").GetPayable(), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, _ := tt.cart.SumGrandTotalWithGiftCards() - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Cart.SumGrandTotalWithGiftCards = %v, want %v", got, tt.want) - } - }) - } -} - func TestCart_HasRemainingGiftCards(t *testing.T) { tests := []struct { name string diff --git a/cart/domain/cart/item.go b/cart/domain/cart/item.go index 1f6a02202..045071cbb 100644 --- a/cart/domain/cart/item.go +++ b/cart/domain/cart/item.go @@ -1,13 +1,9 @@ package cart import ( - "errors" - "fmt" - "math/big" "sort" priceDomain "flamingo.me/flamingo-commerce/v3/price/domain" - "flamingo.me/flamingo-commerce/v3/product/domain" ) type ( @@ -18,7 +14,8 @@ type ( // ExternalReference can be used by cart service implementations to separate the representation in an external // cart service from the unique item ID ExternalReference string - MarketplaceCode string + // MarketplaceCode is the identifier for the product + MarketplaceCode string // VariantMarketPlaceCode is used for Configurable products VariantMarketPlaceCode string ProductName string @@ -30,115 +27,60 @@ type ( AdditionalData map[string]string - // SinglePriceGross gross price (incl. taxes) for a single product + // SinglePriceGross is the gross price (incl. taxes) for a single product SinglePriceGross priceDomain.Price - // SinglePriceNet net price (excl. taxes) for a single product + // SinglePriceNet is the net price (excl. taxes) for a single product SinglePriceNet priceDomain.Price - // RowPriceGross + // RowPriceGross is the price incl. taxes for the whole Qty of products RowPriceGross priceDomain.Price - // RowPriceNet + // RowPriceGrossWithDiscount is the price incl. taxes with deducted discounts for the whole Qty of products + // This is in most cases the final price for the customer to pay + RowPriceGrossWithDiscount priceDomain.Price + + // RowPriceGrossWithItemRelatedDiscount is the price incl. taxes with deducted item related discounts for the whole Qty of products + RowPriceGrossWithItemRelatedDiscount priceDomain.Price + + // RowPriceNet is the price excl. taxes for the whole Qty of products RowPriceNet priceDomain.Price - // RowPriceGross + // RowPriceNetWithDiscount is the discounted net price for the whole Qty of products + RowPriceNetWithDiscount priceDomain.Price + + // RowPriceNetWithItemRelatedDiscount is the price excl. taxes with deducted item related discounts for the whole Qty of products + RowPriceNetWithItemRelatedDiscount priceDomain.Price + + // RowTaxes is a list of all taxes applied for the given Qty of products RowTaxes Taxes // AppliedDiscounts contains the details about the discounts applied to this item - they can be "itemrelated" or not + // itemrelated would be e.g. special price, buy 3 pay 2 + // non-itemrelated would be e.g. 10% on everything AppliedDiscounts AppliedDiscounts - } - // ItemBuilder can be used to construct an item with a fluent interface - ItemBuilder struct { - itemCurrency *string - invariantError error - itemInBuilding *Item - configUseGrossPrice bool - } + // TotalDiscountAmount is the sum of all applied discounts (aka the savings for the customer) + TotalDiscountAmount priceDomain.Price + + // ItemRelatedDiscountAmount is the sum of all itemrelated Discounts + ItemRelatedDiscountAmount priceDomain.Price - // ItemBuilderProvider should be used to create an item - ItemBuilderProvider func() *ItemBuilder + // NonItemRelatedDiscountAmount is the sum of non-itemrelated Discounts where IsItemRelated = false + NonItemRelatedDiscountAmount priceDomain.Price + } // ItemSplitter used to split an item ItemSplitter struct { - itemBuilderProvider ItemBuilderProvider - configUseGrossPrice bool + errorDuringSplitting error } ) -// TotalTaxAmount returns total tax amount as price +// TotalTaxAmount is the sum of all applied taxes for the whole Qty of products func (i Item) TotalTaxAmount() priceDomain.Price { return i.RowTaxes.TotalAmount() } -// TotalDiscountAmount gets the savings by item -func (i Item) TotalDiscountAmount() priceDomain.Price { - result, _ := i.NonItemRelatedDiscountAmount().Add(i.ItemRelatedDiscountAmount()) - - return result -} - -// ItemRelatedDiscountAmount is the sum of AppliedDiscounts where IsItemRelated = true -func (i Item) ItemRelatedDiscountAmount() priceDomain.Price { - prices := make([]priceDomain.Price, 0, len(i.AppliedDiscounts)) - - for _, discount := range i.AppliedDiscounts { - if !discount.IsItemRelated { - continue - } - prices = append(prices, discount.Applied) - } - - result, _ := priceDomain.SumAll(prices...) - - return result.GetPayable() -} - -// NonItemRelatedDiscountAmount is the sum of AppliedDiscounts where IsItemRelated = false -func (i Item) NonItemRelatedDiscountAmount() priceDomain.Price { - prices := make([]priceDomain.Price, 0, len(i.AppliedDiscounts)) - - for _, discount := range i.AppliedDiscounts { - if discount.IsItemRelated { - continue - } - prices = append(prices, discount.Applied) - } - - result, _ := priceDomain.SumAll(prices...) - - return result.GetPayable() -} - -// RowPriceGrossWithDiscount = RowPriceGross-TotalDiscountAmount() -func (i Item) RowPriceGrossWithDiscount() priceDomain.Price { - result, _ := i.RowPriceGross.Add(i.TotalDiscountAmount()) - - return result -} - -// RowPriceNetWithDiscount = RowPriceNet-TotalDiscountAmount() -func (i Item) RowPriceNetWithDiscount() priceDomain.Price { - result, _ := i.RowPriceNet.Add(i.TotalDiscountAmount()) - - return result -} - -// RowPriceGrossWithItemRelatedDiscount = RowPriceGross-ItemRelatedDiscountAmount() -func (i Item) RowPriceGrossWithItemRelatedDiscount() priceDomain.Price { - result, _ := i.RowPriceGross.Add(i.ItemRelatedDiscountAmount()) - - return result -} - -// RowPriceNetWithItemRelatedDiscount =RowTotal-ItemRelatedDiscountAmount -func (i Item) RowPriceNetWithItemRelatedDiscount() priceDomain.Price { - result, _ := i.RowPriceNet.Add(i.ItemRelatedDiscountAmount()) - - return result -} - // AdditionalDataKeys lists all available keys func (i Item) AdditionalDataKeys() []string { additionalData := i.AdditionalData @@ -174,282 +116,6 @@ func (i Item) GetAdditionalData(key string) string { return attribute } -// Inject dependencies -func (f *ItemBuilder) Inject(config *struct { - UseGrosPrice bool `inject:"config:commerce.product.priceIsGross,optional"` -}) { - if config != nil { - f.configUseGrossPrice = config.UseGrosPrice - } -} - -// SetID sets the id -func (f *ItemBuilder) SetID(id string) *ItemBuilder { - f.init() - f.itemInBuilding.ID = id - return f -} - -// SetExternalReference sets the ExternalReference -func (f *ItemBuilder) SetExternalReference(ref string) *ItemBuilder { - f.init() - f.itemInBuilding.ExternalReference = ref - return f -} - -// SetFromItem sets the data in builder from existing item - useful to get a updated item based from existing. Its not setting Taxes (use Calculate) -func (f *ItemBuilder) SetFromItem(item Item) *ItemBuilder { - f.init() - f.SetProductData(item.MarketplaceCode, item.VariantMarketPlaceCode, item.ProductName) - f.SetExternalReference(item.ExternalReference) - f.SetID(item.ID) - f.SetQty(item.Qty) - f.AddDiscounts(item.AppliedDiscounts...) - f.SetSinglePriceGross(item.SinglePriceGross) - f.SetSinglePriceNet(item.SinglePriceNet) - - return f -} - -// SetVariantMarketPlaceCode sets VariantMarketPlaceCode (only for configurable_with_variant relevant) -func (f *ItemBuilder) SetVariantMarketPlaceCode(id string) *ItemBuilder { - f.init() - f.itemInBuilding.VariantMarketPlaceCode = id - return f -} - -// SetSourceID sets optional source ID -func (f *ItemBuilder) SetSourceID(id string) *ItemBuilder { - f.init() - f.itemInBuilding.SourceID = id - return f -} - -// SetAdditionalData sets optional additional data -func (f *ItemBuilder) SetAdditionalData(d map[string]string) *ItemBuilder { - f.init() - f.itemInBuilding.AdditionalData = d - return f -} - -// SetQty sets the qty (defaults to 1) -func (f *ItemBuilder) SetQty(q int) *ItemBuilder { - f.init() - f.itemInBuilding.Qty = q - return f -} - -// SetSinglePriceGross set by gross price -func (f *ItemBuilder) SetSinglePriceGross(grossPrice priceDomain.Price) *ItemBuilder { - f.init() - if !grossPrice.IsPayable() { - f.invariantError = errors.New("SetSinglePriceGross need to get payable price") - } - f.itemInBuilding.SinglePriceGross = grossPrice - f.checkCurrency(&grossPrice) - return f -} - -// SetSinglePriceNet set by net price -func (f *ItemBuilder) SetSinglePriceNet(price priceDomain.Price) *ItemBuilder { - f.init() - if !price.IsPayable() { - f.invariantError = errors.New("SetSinglePriceNet need to get payable price") - } - f.itemInBuilding.SinglePriceNet = price - f.checkCurrency(&price) - return f -} - -// AddTaxInfo add a tax info - at least taxRate OR taxAmount need to be given. the tax amount can be calculated -func (f *ItemBuilder) AddTaxInfo(taxType string, taxRate *big.Float, taxAmount *priceDomain.Price) *ItemBuilder { - f.init() - if taxRate == nil && taxAmount == nil { - f.invariantError = errors.New("at least taxRate or taxAmount need to be given") - } - tax := Tax{ - Type: taxType, - Rate: taxRate, - } - if taxAmount != nil { - if !taxAmount.IsPayable() { - f.invariantError = errors.New("taxAmount need to be payable price") - } - f.checkCurrency(taxAmount) - tax.Amount = *taxAmount - } - f.itemInBuilding.RowTaxes = append(f.itemInBuilding.RowTaxes, tax) - return f -} - -// AddDiscount adds a discount -func (f *ItemBuilder) AddDiscount(discount AppliedDiscount) *ItemBuilder { - f.init() - if !discount.Applied.IsPayable() { - f.invariantError = errors.New("AddDiscount need to have payable price") - } - if !discount.Applied.IsNegative() { - f.invariantError = fmt.Errorf("AddDiscount need to have negative price - given %f", discount.Applied.FloatAmount()) - } - f.checkCurrency(&discount.Applied) - f.itemInBuilding.AppliedDiscounts = append(f.itemInBuilding.AppliedDiscounts, discount) - return f -} - -// AddDiscounts adds a list of discounts -func (f *ItemBuilder) AddDiscounts(discounts ...AppliedDiscount) *ItemBuilder { - for _, discount := range discounts { - f.AddDiscount(discount) - } - return f -} - -// CalculatePricesAndTaxAmountsFromSinglePriceNet handles the vertical tax calculation - based from current SinglePriceNet, Qty and the RowTax Infos given -// Sets RowPriceNet, missing tax.Amount and RowPriceGross -func (f *ItemBuilder) CalculatePricesAndTaxAmountsFromSinglePriceNet() *ItemBuilder { - priceNet := f.itemInBuilding.SinglePriceNet - f.itemInBuilding.RowPriceNet = priceNet.Multiply(f.itemInBuilding.Qty) - for k, tax := range f.itemInBuilding.RowTaxes { - // Calculate tax amount from rate if required - if tax.Amount.IsZero() && tax.Rate != nil { - // set tax amount and round it - tax.Amount = f.itemInBuilding.RowPriceNetWithDiscount().TaxFromNet(*tax.Rate).GetPayable() - f.itemInBuilding.RowTaxes[k] = tax - } - } - totalTaxAmount := f.itemInBuilding.TotalTaxAmount() - f.itemInBuilding.RowPriceGross, _ = priceDomain.SumAll(f.itemInBuilding.RowPriceNet, totalTaxAmount) - if f.itemInBuilding.Qty == 0 { - f.invariantError = errors.New("Quantity is Zero") - return f - } - f.itemInBuilding.SinglePriceGross, _ = priceNet.Add(totalTaxAmount.Divided(f.itemInBuilding.Qty)) - return f -} - -// CalculatePricesAndTax reads the config flag and recalculates Total and Tax -func (f *ItemBuilder) CalculatePricesAndTax() *ItemBuilder { - if f.configUseGrossPrice { - return f.CalculatePricesAndTaxAmountsFromSinglePriceGross() - } - return f.CalculatePricesAndTaxAmountsFromSinglePriceNet() -} - -// CalculatePricesAndTaxAmountsFromSinglePriceGross handles the vertical tax calculation - based from current SinglePriceNet, Qty and the RowTax Infos given -// Sets RowPriceNet, missing tax.Amount and RowPriceGross -func (f *ItemBuilder) CalculatePricesAndTaxAmountsFromSinglePriceGross() *ItemBuilder { - priceGross := f.itemInBuilding.SinglePriceGross - f.itemInBuilding.RowPriceGross = priceGross.Multiply(f.itemInBuilding.Qty) - for k, tax := range f.itemInBuilding.RowTaxes { - // Calculate tax amount from rate if required - if tax.Amount.IsZero() && tax.Rate != nil { - tax.Amount = f.itemInBuilding.RowPriceGrossWithDiscount().TaxFromGross(*tax.Rate).GetPayable() - f.itemInBuilding.RowTaxes[k] = tax - } - } - totalTaxAmount := f.itemInBuilding.TotalTaxAmount() - f.itemInBuilding.RowPriceNet, _ = f.itemInBuilding.RowPriceGross.Sub(totalTaxAmount) - f.itemInBuilding.SinglePriceNet, _ = priceGross.Sub(totalTaxAmount.Divided(f.itemInBuilding.Qty)) - return f -} - -// SetProductData set product data: MarketplaceCode, VariantMarketPlaceCode, ProductName -func (f *ItemBuilder) SetProductData(marketplace string, vc string, name string) *ItemBuilder { - f.init() - f.itemInBuilding.MarketplaceCode = marketplace - f.itemInBuilding.VariantMarketPlaceCode = vc - f.itemInBuilding.ProductName = name - return f -} - -// SetByProduct gets a product and calculates also prices -func (f *ItemBuilder) SetByProduct(product domain.BasicProduct) *ItemBuilder { - if !product.IsSaleable() { - f.invariantError = errors.New("Product is not saleable") - } - - f.init() - f.itemInBuilding.MarketplaceCode = product.BaseData().MarketPlaceCode - f.itemInBuilding.ProductName = product.BaseData().Title - - if configurable, ok := product.(domain.ConfigurableProductWithActiveVariant); ok { - f.itemInBuilding.MarketplaceCode = configurable.ConfigurableBaseData().MarketPlaceCode - f.itemInBuilding.VariantMarketPlaceCode = configurable.ActiveVariant.MarketPlaceCode - } - - if f.configUseGrossPrice { - f.SetSinglePriceGross(product.SaleableData().ActivePrice.GetFinalPrice()) - f.CalculatePricesAndTaxAmountsFromSinglePriceGross() - } else { - f.SetSinglePriceNet(product.SaleableData().ActivePrice.GetFinalPrice()) - f.CalculatePricesAndTaxAmountsFromSinglePriceNet() - } - - return f -} - -func (f *ItemBuilder) checkCurrency(price *priceDomain.Price) { - if price == nil { - return - } - currency := price.Currency() - if f.itemCurrency == nil { - f.itemCurrency = ¤cy - return - } - if *f.itemCurrency != currency { - f.invariantError = fmt.Errorf("There is a currency mismatch inside the item %v and %v", currency, *f.itemCurrency) - } -} - -// Build returns build item or error if invariants do not match. Any call will also REST the ItemBuilder -func (f *ItemBuilder) Build() (*Item, error) { - if f.itemInBuilding == nil { - return f.reset(errors.New("Nothing in building")) - } - - if f.invariantError != nil { - return f.reset(f.invariantError) - } - - if f.itemInBuilding.ID == "" { - return f.reset(errors.New("Id Required")) - } - - checkPrice, _ := f.itemInBuilding.RowPriceNet.Add(f.itemInBuilding.TotalTaxAmount()) - if !checkPrice.LikelyEqual(f.itemInBuilding.RowPriceGross) { - return f.reset(fmt.Errorf("RowPriceGross (%f) need to match likely TotalTaxAmount + RowPriceNet. (%f) for item %v ", f.itemInBuilding.RowPriceGross.FloatAmount(), checkPrice.FloatAmount(), f.itemInBuilding.ID)) - } - - return f.reset(nil) -} - -func (f *ItemBuilder) init() { - if f.itemInBuilding == nil { - f.itemInBuilding = &Item{ - Qty: 1, - } - } -} - -func (f *ItemBuilder) reset(err error) (*Item, error) { - item := f.itemInBuilding - f.itemInBuilding = nil - f.invariantError = nil - f.itemCurrency = nil - return item, err -} - -// Inject dependencies -func (s *ItemSplitter) Inject(itemBuilderProvider ItemBuilderProvider, config *struct { - UseGrossPrice bool `inject:"config:commerce.product.priceIsGross,optional"` -}) { - s.itemBuilderProvider = itemBuilderProvider - if config != nil { - s.configUseGrossPrice = config.UseGrossPrice - } -} - // SplitInSingleQtyItems the given item into multiple items with Qty 1 and make sure the sum of the items prices matches by using SplitInPayables func (s *ItemSplitter) SplitInSingleQtyItems(givenItem Item) ([]Item, error) { var items []Item @@ -461,39 +127,58 @@ func (s *ItemSplitter) SplitInSingleQtyItems(givenItem Item) ([]Item, error) { // Given: SinglePriceNez / all AppliedDiscounts / All Taxes // Calculated: SinglePriceGross / RowPriceGross / RowPriceNet / SinglePriceGross for x := 0; x < givenItem.Qty; x++ { + item := Item{ + MarketplaceCode: givenItem.MarketplaceCode, + VariantMarketPlaceCode: givenItem.VariantMarketPlaceCode, + ProductName: givenItem.ProductName, + ExternalReference: givenItem.ExternalReference, + ID: givenItem.ID, + SourceID: givenItem.SourceID, + AdditionalData: givenItem.AdditionalData, + Qty: 1, + TotalDiscountAmount: priceDomain.NewZero(givenItem.SinglePriceGross.Currency()), + ItemRelatedDiscountAmount: priceDomain.NewZero(givenItem.SinglePriceGross.Currency()), + NonItemRelatedDiscountAmount: priceDomain.NewZero(givenItem.SinglePriceGross.Currency()), + } - itemBuilder := s.itemBuilderProvider() - itemBuilder.SetProductData(givenItem.MarketplaceCode, givenItem.VariantMarketPlaceCode, givenItem.ProductName) - itemBuilder.SetExternalReference(givenItem.ExternalReference) - itemBuilder.SetID(givenItem.ID) - itemBuilder.SetQty(1) for _, ap := range givenItem.AppliedDiscounts { - apSplitted, err := ap.Applied.SplitInPayables(givenItem.Qty) + apSplit, err := ap.Applied.SplitInPayables(givenItem.Qty) + if err != nil { + return nil, err + } + // The split adds the moving cents to the first ones, resulting in // having the smallest prices at the end but since discounts are // negative, we need to reverse it to ensure that a split of the row // totals has the rounding cents at the same positions - sort.Slice(apSplitted, func(i, j int) bool { - return apSplitted[i].FloatAmount() > apSplitted[j].FloatAmount() + sort.Slice(apSplit, func(i, j int) bool { + return apSplit[i].FloatAmount() > apSplit[j].FloatAmount() }) - p := make([]float64, 0) - for _, i := range apSplitted { - p = append(p, i.FloatAmount()) - } - if err != nil { - return nil, err - } + newDiscount := AppliedDiscount{ CampaignCode: ap.CampaignCode, CouponCode: ap.CouponCode, Label: ap.Label, - Applied: apSplitted[x], + Applied: apSplit[x], Type: ap.Type, IsItemRelated: ap.IsItemRelated, SortOrder: ap.SortOrder, } - itemBuilder.AddDiscount(newDiscount) + + if ap.IsItemRelated { + item.ItemRelatedDiscountAmount = item.ItemRelatedDiscountAmount.ForceAdd(apSplit[x]) + } else { + item.NonItemRelatedDiscountAmount = item.NonItemRelatedDiscountAmount.ForceAdd(apSplit[x]) + } + + item.TotalDiscountAmount, err = item.TotalDiscountAmount.Add(apSplit[x]) + if err != nil { + return nil, err + } + + item.AppliedDiscounts = append(item.AppliedDiscounts, newDiscount) } + for _, rt := range givenItem.RowTaxes { if rt.Amount.IsZero() { continue @@ -502,20 +187,47 @@ func (s *ItemSplitter) SplitInSingleQtyItems(givenItem Item) ([]Item, error) { if err != nil { return nil, err } - itemBuilder.AddTaxInfo(rt.Type, rt.Rate, &rtSplitted[x]) + + sort.Slice(rtSplitted, func(i, j int) bool { + return rtSplitted[i].FloatAmount() < rtSplitted[j].FloatAmount() + }) + newTax := Tax{ + Type: rt.Type, + Rate: rt.Rate, + Amount: rtSplitted[x], + } + + item.RowTaxes = append(item.RowTaxes, newTax) } - if s.configUseGrossPrice { - itemBuilder.SetSinglePriceGross(givenItem.SinglePriceGross.GetPayable()) - itemBuilder.CalculatePricesAndTaxAmountsFromSinglePriceGross() - } else { - itemBuilder.SetSinglePriceNet(givenItem.SinglePriceNet.GetPayable()) - itemBuilder.CalculatePricesAndTaxAmountsFromSinglePriceNet() + + item.SinglePriceGross, item.SinglePriceNet = givenItem.SinglePriceGross, givenItem.SinglePriceNet + item.RowPriceGross, item.RowPriceNet = item.SinglePriceGross, item.SinglePriceNet + + item.RowPriceNetWithDiscount = s.splitPrice(givenItem.RowPriceNetWithDiscount, givenItem.Qty, x) + taxAmount := item.RowTaxes.TotalAmount() + item.RowPriceGrossWithDiscount = item.RowPriceNetWithDiscount + if !taxAmount.IsZero() && taxAmount.Currency() == item.RowPriceGrossWithDiscount.Currency() { + item.RowPriceGrossWithDiscount = item.RowPriceGrossWithDiscount.ForceAdd(taxAmount) } - item, err := itemBuilder.Build() - if err != nil { - return nil, err + + item.RowPriceGrossWithItemRelatedDiscount = s.splitPrice(givenItem.RowPriceGrossWithItemRelatedDiscount, givenItem.Qty, x) + item.RowPriceNetWithItemRelatedDiscount = s.splitPrice(givenItem.RowPriceNetWithItemRelatedDiscount, givenItem.Qty, x) + + if s.errorDuringSplitting != nil { + return nil, s.errorDuringSplitting } - items = append(items, *item) + + items = append(items, item) } return items, nil } + +func (s *ItemSplitter) splitPrice(givenPrice priceDomain.Price, qty int, splitPosition int) priceDomain.Price { + splitted, err := givenPrice.SplitInPayables(qty) + if err != nil { + s.errorDuringSplitting = err + return priceDomain.Price{} + } + + return splitted[splitPosition] +} diff --git a/cart/domain/cart/item_test.go b/cart/domain/cart/item_test.go index 699385222..620dde48b 100644 --- a/cart/domain/cart/item_test.go +++ b/cart/domain/cart/item_test.go @@ -1,7 +1,6 @@ package cart_test import ( - "fmt" "math/big" "testing" @@ -13,130 +12,215 @@ import ( priceDomain "flamingo.me/flamingo-commerce/v3/price/domain" ) -func TestItem_PriceCalculation(t *testing.T) { +func TestItemSplitter_SplitGrossBased(t *testing.T) { + splitter := &cartDomain.ItemSplitter{} - item := cartDomain.Item{ - SinglePriceNet: priceDomain.NewFromInt(1234, 100, "EUR"), - SinglePriceGross: priceDomain.NewFromInt(1247, 100, "EUR"), - AppliedDiscounts: []cartDomain.AppliedDiscount{ - { - Applied: priceDomain.NewFromInt(-100, 100, "EUR"), - IsItemRelated: true, + items := []*cartDomain.Item{ + { + ID: "2", + ExternalReference: "external", + MarketplaceCode: "item related discount", + VariantMarketPlaceCode: "variant", + ProductName: "product", + SourceID: "warehouse1", + Qty: 5, + SinglePriceGross: priceDomain.NewFromInt(2065, 100, "€"), + SinglePriceNet: priceDomain.NewFromInt(1930, 100, "€"), + RowPriceGross: priceDomain.NewFromInt(2065*5, 100, "€"), + RowPriceGrossWithDiscount: priceDomain.NewFromInt(2065*5-3172, 100, "€"), + RowPriceGrossWithItemRelatedDiscount: priceDomain.NewFromInt(2065*5, 100, "€"), + RowPriceNet: priceDomain.NewFromInt(1930*5, 100, "€"), + RowPriceNetWithDiscount: priceDomain.NewFromInt(6685, 100, "€"), + RowPriceNetWithItemRelatedDiscount: priceDomain.NewFromInt(1930*5, 100, "€"), + RowTaxes: []cartDomain.Tax{{Amount: priceDomain.NewFromInt(468, 100, "€"), Type: "tax", Rate: big.NewFloat(7)}}, + AppliedDiscounts: cartDomain.AppliedDiscounts{ + cartDomain.AppliedDiscount{IsItemRelated: true, Applied: priceDomain.NewFromInt(-3172, 100, "€")}, }, - { - Applied: priceDomain.NewFromInt(-200, 100, "EUR"), - IsItemRelated: false, + TotalDiscountAmount: priceDomain.NewFromInt(-3172, 100, "€"), + ItemRelatedDiscountAmount: priceDomain.NewFromInt(-3172, 100, "€"), + NonItemRelatedDiscountAmount: priceDomain.NewFromInt(0, 100, "€"), + AdditionalData: map[string]string{"foo": "bar"}, + }, + { + ID: "2", + ExternalReference: "external", + MarketplaceCode: "non item related discount", + VariantMarketPlaceCode: "variant", + ProductName: "product", + SourceID: "warehouse1", + Qty: 5, + SinglePriceGross: priceDomain.NewFromInt(2065, 100, "€"), + SinglePriceNet: priceDomain.NewFromInt(1930, 100, "€"), + RowPriceGross: priceDomain.NewFromInt(2065*5, 100, "€"), + RowPriceGrossWithDiscount: priceDomain.NewFromInt(2065*5-3172, 100, "€"), + RowPriceGrossWithItemRelatedDiscount: priceDomain.NewFromInt(2065*5, 100, "€"), + RowPriceNet: priceDomain.NewFromInt(1930*5, 100, "€"), + RowPriceNetWithDiscount: priceDomain.NewFromInt(6685, 100, "€"), + RowPriceNetWithItemRelatedDiscount: priceDomain.NewFromInt(1930*5, 100, "€"), + RowTaxes: []cartDomain.Tax{{Amount: priceDomain.NewFromInt(468, 100, "€"), Type: "tax", Rate: big.NewFloat(7)}}, + AppliedDiscounts: cartDomain.AppliedDiscounts{ + cartDomain.AppliedDiscount{IsItemRelated: false, Applied: priceDomain.NewFromInt(-3172, 100, "€")}, }, + TotalDiscountAmount: priceDomain.NewFromInt(-3172, 100, "€"), + ItemRelatedDiscountAmount: priceDomain.NewFromInt(0, 100, "€"), + NonItemRelatedDiscountAmount: priceDomain.NewFromInt(-3172, 100, "€"), + AdditionalData: map[string]string{"baz": "bam"}, + }, + { + Qty: 2, + MarketplaceCode: "foo", + SinglePriceNet: priceDomain.NewFromFloat(42.01, "EUR"), + SinglePriceGross: priceDomain.NewFromFloat(49.99, "EUR"), + RowPriceNet: priceDomain.NewFromFloat(84.02, "EUR"), + RowPriceGross: priceDomain.NewFromFloat(99.98, "EUR"), + RowPriceNetWithDiscount: priceDomain.NewFromFloat(75.61, "EUR"), + RowPriceGrossWithDiscount: priceDomain.NewFromFloat(89.98, "EUR"), + RowTaxes: []cartDomain.Tax{{ + Amount: priceDomain.NewFromFloat(14.37, "EUR"), + Rate: big.NewFloat(19.0), + Type: "VAT", + }}, + TotalDiscountAmount: priceDomain.NewFromFloat(-10.00, "EUR"), + NonItemRelatedDiscountAmount: priceDomain.NewFromFloat(-10.00, "EUR"), + ItemRelatedDiscountAmount: priceDomain.NewZero("EUR"), + RowPriceNetWithItemRelatedDiscount: priceDomain.NewFromFloat(84.02, "EUR"), + RowPriceGrossWithItemRelatedDiscount: priceDomain.NewFromFloat(99.98, "EUR"), + AppliedDiscounts: []cartDomain.AppliedDiscount{{Applied: priceDomain.NewFromFloat(-10.00, "EUR")}}, + }, + { + Qty: 1, + MarketplaceCode: "no need for split, no taxes, no discounts", + SinglePriceNet: priceDomain.NewFromFloat(42.01, "EUR"), + SinglePriceGross: priceDomain.NewFromFloat(42.01, "EUR"), + RowPriceNet: priceDomain.NewFromFloat(42.01, "EUR"), + RowPriceGross: priceDomain.NewFromFloat(42.01, "EUR"), + RowPriceNetWithDiscount: priceDomain.NewFromFloat(42.01, "EUR"), + RowPriceGrossWithDiscount: priceDomain.NewFromFloat(42.01, "EUR"), + TotalDiscountAmount: priceDomain.NewZero("EUR"), + NonItemRelatedDiscountAmount: priceDomain.NewZero("EUR"), + ItemRelatedDiscountAmount: priceDomain.NewZero("EUR"), + RowPriceNetWithItemRelatedDiscount: priceDomain.NewZero("EUR"), + RowPriceGrossWithItemRelatedDiscount: priceDomain.NewZero("EUR"), }, - RowPriceNet: priceDomain.NewFromInt(12340, 100, "EUR"), - RowPriceGross: priceDomain.NewFromInt(12470, 100, "EUR"), - RowTaxes: cartDomain.Taxes([]cartDomain.Tax{ - {Amount: priceDomain.NewFromInt(130, 100, "EUR"), Type: "vat"}, - }), - Qty: 10, } - assert.Equal(t, item.ItemRelatedDiscountAmount(), priceDomain.NewFromInt(-100, 100, "EUR"), "ItemRelatedDiscountAmount") - assert.Equal(t, item.NonItemRelatedDiscountAmount(), priceDomain.NewFromInt(-200, 100, "EUR"), "NonItemRelatedDiscountAmount") - assert.Equal(t, item.TotalDiscountAmount(), priceDomain.NewFromInt(-300, 100, "EUR"), "TotalDiscountAmount") - - assertPricesWithLikelyEqual(t, item.RowPriceGrossWithDiscount(), priceDomain.NewFromInt(12170, 100, "EUR"), "RowPriceGrossWithDiscount") - assertPricesWithLikelyEqual(t, item.RowPriceNetWithDiscount(), priceDomain.NewFromInt(12040, 100, "EUR"), "RowPriceNetWithDiscount") - assertPricesWithLikelyEqual(t, item.RowPriceNetWithItemRelatedDiscount(), priceDomain.NewFromInt(12240, 100, "EUR"), "RowPriceNetWithItemRelatedDiscount") - - assert.Equal(t, 1, len(item.RowTaxes)) - assertPricesWithLikelyEqual(t, item.RowTaxes.TotalAmount(), priceDomain.NewFromInt(130, 100, "EUR"), "RowTaxes") - + for _, item := range items { + t.Run(item.MarketplaceCode, func(t *testing.T) { + splitItems, err := splitter.SplitInSingleQtyItems(*item) + require.NoError(t, err) + assert.Len(t, splitItems, item.Qty) + + var ( + discount, + rowGrossTotal, + rowGrossWithDiscount, + rowGrossWithItemDiscount, + rowNetWithDiscounts, + rowNetWithItemDiscount, + itemDiscountAmount, + nonItemDiscountAmount, + rowNetTotal, + totalTaxAmount, + totalDiscountAmount float64 + ) + appliedDiscounts := make([]float64, len(item.AppliedDiscounts)) + for _, splitItem := range splitItems { + assert.Equal(t, item.ID, splitItem.ID, "ID") + assert.Equal(t, item.ExternalReference, splitItem.ExternalReference, "ExternalReference") + assert.Equal(t, item.MarketplaceCode, splitItem.MarketplaceCode, "MarketplaceCode") + assert.Equal(t, item.VariantMarketPlaceCode, splitItem.VariantMarketPlaceCode, "VariantMarketPlaceCode") + assert.Equal(t, item.ProductName, splitItem.ProductName, "ProductName") + assert.Equal(t, item.SourceID, splitItem.SourceID, "SourceID") + assert.Equal(t, item.AdditionalData, splitItem.AdditionalData) + assert.Equal(t, 1, splitItem.Qty) + // make sure single and row price are equal: + assert.Equal(t, splitItem.SinglePriceNet.FloatAmount(), splitItem.RowPriceNet.FloatAmount()) + assert.Equal(t, splitItem.SinglePriceGross.FloatAmount(), splitItem.RowPriceGross.FloatAmount()) + // make sure it's consistent (net+tax=gross): + assert.Equal(t, splitItem.RowPriceGrossWithDiscount.FloatAmount(), splitItem.RowPriceNetWithDiscount.ForceAdd(splitItem.TotalTaxAmount()).FloatAmount()) + + rowGrossTotal += splitItem.RowPriceGross.FloatAmount() + rowNetTotal += splitItem.RowPriceNet.FloatAmount() + totalTaxAmount += splitItem.TotalTaxAmount().FloatAmount() + rowGrossWithDiscount += splitItem.RowPriceGrossWithDiscount.FloatAmount() + rowGrossWithItemDiscount += splitItem.RowPriceGrossWithItemRelatedDiscount.FloatAmount() + rowNetWithDiscounts += splitItem.RowPriceNetWithDiscount.FloatAmount() + rowNetWithItemDiscount += splitItem.RowPriceNetWithItemRelatedDiscount.FloatAmount() + itemDiscountAmount += splitItem.ItemRelatedDiscountAmount.FloatAmount() + nonItemDiscountAmount += splitItem.NonItemRelatedDiscountAmount.FloatAmount() + if len(splitItem.RowTaxes) > 0 { + rate, _ := splitItem.RowTaxes[0].Rate.Float64() + expectedRate, _ := item.RowTaxes[0].Rate.Float64() + assert.Equal(t, expectedRate, rate) + } + totalDiscountAmount = totalDiscountAmount + splitItem.TotalDiscountAmount.FloatAmount() + + require.Len(t, splitItem.AppliedDiscounts, len(item.AppliedDiscounts)) + for i, appliedDiscount := range splitItem.AppliedDiscounts { + appliedDiscounts[i] += appliedDiscount.Applied.FloatAmount() + assert.Equal(t, item.AppliedDiscounts[i].CampaignCode, appliedDiscount.CampaignCode) + assert.Equal(t, item.AppliedDiscounts[i].IsItemRelated, appliedDiscount.IsItemRelated) + assert.Equal(t, item.AppliedDiscounts[i].CouponCode, appliedDiscount.CouponCode) + assert.Equal(t, item.AppliedDiscounts[i].Type, appliedDiscount.Type) + assert.Equal(t, item.AppliedDiscounts[i].SortOrder, appliedDiscount.SortOrder) + assert.Equal(t, item.AppliedDiscounts[i].Label, appliedDiscount.Label) + } + + // discount split cents should be at the end, so the next discount must be the same or smaller + assert.GreaterOrEqual(t, discount, splitItem.TotalDiscountAmount.FloatAmount()) + discount = splitItem.TotalDiscountAmount.FloatAmount() + } + + assert.Equal(t, item.RowPriceGrossWithDiscount.FloatAmount(), rowGrossWithDiscount) + assert.Equal(t, item.RowPriceGrossWithItemRelatedDiscount.FloatAmount(), rowGrossWithItemDiscount) + assert.Equal(t, item.RowPriceNetWithDiscount.FloatAmount(), rowNetWithDiscounts) + assert.Equal(t, item.RowPriceNetWithItemRelatedDiscount.FloatAmount(), rowNetWithItemDiscount) + assert.Equal(t, item.ItemRelatedDiscountAmount.FloatAmount(), itemDiscountAmount) + assert.Equal(t, item.NonItemRelatedDiscountAmount.FloatAmount(), nonItemDiscountAmount) + assert.Equal(t, item.TotalDiscountAmount.FloatAmount(), totalDiscountAmount) + for i, appliedDiscount := range item.AppliedDiscounts { + assert.Equal(t, appliedDiscount.Applied.FloatAmount(), appliedDiscounts[i]) + } + + assert.Equal(t, item.RowPriceGross.FloatAmount(), rowGrossTotal) + assert.Equal(t, item.RowPriceNet.FloatAmount(), rowNetTotal) + assert.True(t, item.TotalTaxAmount().LikelyEqual(priceDomain.NewFromFloat(totalTaxAmount, item.TotalTaxAmount().Currency()))) + }) + } } -func TestItemBuild_SimpleBuild(t *testing.T) { - - f := &cartDomain.ItemBuilder{} - item, err := f.SetSinglePriceNet(priceDomain.NewFromInt(100, 100, "EUR")).SetQty(10).SetID("22").SetExternalReference("kkk").CalculatePricesAndTaxAmountsFromSinglePriceNet().Build() - assert.NoError(t, err) - assert.Equal(t, "22", item.ID) - assert.Equal(t, priceDomain.NewFromInt(1000, 100, "EUR"), item.RowPriceGross) - - // with tax from net: - item, err = f.SetSinglePriceNet(priceDomain.NewFromInt(100, 100, "EUR")).SetQty(10).SetID("22").SetExternalReference("kkk").AddTaxInfo("default", big.NewFloat(10), nil).CalculatePricesAndTaxAmountsFromSinglePriceNet().Build() - assert.NoError(t, err) - assert.Equal(t, "22", item.ID) - assert.Equal(t, priceDomain.NewFromInt(1100, 100, "EUR"), item.RowPriceGross) - - // with tax from gross: - item, err = f.SetSinglePriceGross(priceDomain.NewFromInt(110, 100, "EUR")).SetQty(10).SetID("22").SetExternalReference("kkk").AddTaxInfo("default", big.NewFloat(10), nil).CalculatePricesAndTaxAmountsFromSinglePriceGross().Build() - assert.NoError(t, err) - assert.Equal(t, "22", item.ID) - assertPricesWithLikelyEqual(t, priceDomain.NewFromInt(1100, 100, "EUR"), item.RowPriceGross, "RowPriceGross wrong") - assert.Equal(t, priceDomain.NewFromInt(100, 100, "EUR"), item.TotalTaxAmount()) +func TestItem_AdditionalDataKeys(t *testing.T) { + item := cartDomain.Item{ + ID: "2", + AdditionalData: map[string]string{"foo": "bar", "baz": "bam"}, + } + assert.ElementsMatch(t, []string{"foo", "baz"}, item.AdditionalDataKeys()) } -func assertPricesWithLikelyEqual(t *testing.T, p1 priceDomain.Price, p2 priceDomain.Price, msg string) { - assert.True(t, p1.LikelyEqual(p2), fmt.Sprintf("%v (%f != %f)", msg, p1.FloatAmount(), p2.FloatAmount())) +func TestItem_AdditionalDataValues(t *testing.T) { + item := cartDomain.Item{ + ID: "2", + AdditionalData: map[string]string{"foo": "bar", "baz": "bam"}, + } + assert.ElementsMatch(t, []string{"bar", "bam"}, item.AdditionalDataValues()) } -func TestItemSplitter_SplitGrossBased(t *testing.T) { - provider := func() *cartDomain.ItemBuilder { - b := cartDomain.ItemBuilder{} - b.Inject(&struct { - UseGrosPrice bool `inject:"config:commerce.product.priceIsGross,optional"` - }{ - UseGrosPrice: true, - }) - return &b +func TestItem_HasAdditionalDataKey(t *testing.T) { + item := cartDomain.Item{ + ID: "2", + AdditionalData: map[string]string{"foo": "bar"}, } - splitter := &cartDomain.ItemSplitter{} - splitter.Inject(provider, &struct { - UseGrossPrice bool `inject:"config:commerce.product.priceIsGross,optional"` - }{ - UseGrossPrice: true, - }) - - builder := provider() - builder.SetSinglePriceGross(priceDomain.NewFromInt(2065, 100, "€")). - SetQty(5).AddTaxInfo("tax", big.NewFloat(7), nil). - SetID("2"). - AddDiscount(cartDomain.AppliedDiscount{Applied: priceDomain.NewFromInt(-3172, 100, "€")}). - CalculatePricesAndTaxAmountsFromSinglePriceGross() - item, err := builder.Build() - require.NoError(t, err) - - splittedItems, err := splitter.SplitInSingleQtyItems(*item) - require.NoError(t, err) - - // 20.65 * 5 = 103.25 - assert.Equal(t, 103.25, item.RowPriceGross.FloatAmount()) - assert.Equal(t, -31.72, item.TotalDiscountAmount().FloatAmount()) - // (98.57 - 31.70) * 0.07 - assert.Equal(t, 4.68, item.TotalTaxAmount().FloatAmount()) - // TotalTaxAmount + 98.57 = 103.25 - assert.Equal(t, 98.57, item.RowPriceNet.FloatAmount()) - assert.Equal(t, 66.85, item.RowPriceNetWithDiscount().FloatAmount()) - - var discount, rowGrossTotal, rowNetTotal, totalTaxAmount, totalDiscountAmount float64 - for _, splitItem := range splittedItems { - assert.Equal(t, 1, splitItem.Qty) - // make sure single and row price are equal: - assert.Equal(t, splitItem.SinglePriceNet.FloatAmount(), splitItem.RowPriceNet.FloatAmount()) - assert.Equal(t, splitItem.SinglePriceGross.FloatAmount(), splitItem.RowPriceGross.FloatAmount()) - // make sure it's consistent (net+tax=gross): - assert.Equal(t, splitItem.RowPriceGross.FloatAmount(), splitItem.RowPriceNet.ForceAdd(splitItem.TotalTaxAmount()).FloatAmount()) - rowGrossTotal = rowGrossTotal + splitItem.RowPriceGross.FloatAmount() - rowNetTotal = rowNetTotal + splitItem.RowPriceNet.FloatAmount() - totalTaxAmount = totalTaxAmount + splitItem.TotalTaxAmount().FloatAmount() - rate, _ := splitItem.RowTaxes[0].Rate.Float64() - assert.Equal(t, 7.0, rate) - totalDiscountAmount = totalDiscountAmount + splitItem.TotalDiscountAmount().FloatAmount() - - // discount split cents should be at the end, so the next discount must be the same or smaller - assert.GreaterOrEqual(t, discount, splitItem.TotalDiscountAmount().FloatAmount()) - discount = splitItem.TotalDiscountAmount().FloatAmount() + assert.True(t, item.HasAdditionalDataKey("foo")) + assert.False(t, item.HasAdditionalDataKey("bam")) +} +func TestItem_GetAdditionalData(t *testing.T) { + item := cartDomain.Item{ + ID: "2", + AdditionalData: map[string]string{"foo": "bar"}, } - assert.Equal(t, item.RowPriceGross.FloatAmount(), rowGrossTotal) - assert.Equal(t, item.RowPriceNet.FloatAmount(), rowNetTotal) - assert.Equal(t, item.TotalTaxAmount().FloatAmount(), totalTaxAmount) - assert.Equal(t, item.TotalDiscountAmount().FloatAmount(), totalDiscountAmount) + assert.Equal(t, "bar", item.GetAdditionalData("foo")) + assert.Equal(t, "", item.GetAdditionalData("bar")) } diff --git a/cart/domain/cart/paymentselection_wb_test.go b/cart/domain/cart/paymentselection_wb_test.go index 2311f00a0..37bb35d1a 100644 --- a/cart/domain/cart/paymentselection_wb_test.go +++ b/cart/domain/cart/paymentselection_wb_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "flamingo.me/flamingo-commerce/v3/price/domain" ) @@ -18,7 +19,6 @@ func getPaymentMethodMapping(t *testing.T) map[string]string { } func Test_CanBuildSimpleSelectionFromCard(t *testing.T) { - cart := Cart{ Deliveries: []Delivery{ { @@ -27,17 +27,16 @@ func Test_CanBuildSimpleSelectionFromCard(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(199, 100, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(199, 100, "€"), }, { - ID: "2", - RowPriceGross: domain.NewFromInt(299, 100, "€"), + ID: "2", + RowPriceGrossWithDiscount: domain.NewFromInt(299, 100, "€"), }, }, ShippingItem: ShippingItem{ - PriceNet: domain.NewFromInt(7, 1, "€"), - PriceGross: domain.NewFromInt(7, 1, "€"), + PriceGrossWithDiscounts: domain.NewFromInt(7, 1, "€"), }, }, }, @@ -55,24 +54,23 @@ func Test_CanBuildSimpleSelectionWithGiftCard_NoGc(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(199, 100, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(199, 100, "€"), }, { - ID: "2", - RowPriceGross: domain.NewFromInt(299, 100, "€"), + ID: "2", + RowPriceGrossWithDiscount: domain.NewFromInt(299, 100, "€"), }, }, ShippingItem: ShippingItem{ - PriceNet: domain.NewFromInt(7, 1, "€"), - PriceGross: domain.NewFromInt(7, 1, "€"), + PriceGrossWithDiscounts: domain.NewFromInt(7, 1, "€"), }, }, }, AppliedGiftCards: AppliedGiftCards{}, } selection, err := NewDefaultPaymentSelection("gateyway", getPaymentMethodMapping(t), cart) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, domain.NewFromInt(1198, 100, "€").FloatAmount(), selection.TotalValue().FloatAmount()) } @@ -86,17 +84,16 @@ func Test_CanBuildSimpleSelectionWithGiftCard(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(199, 100, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(199, 100, "€"), }, { - ID: "2", - RowPriceGross: domain.NewFromInt(299, 100, "€"), + ID: "2", + RowPriceGrossWithDiscount: domain.NewFromInt(299, 100, "€"), }, }, ShippingItem: ShippingItem{ - PriceNet: domain.NewFromInt(7, 1, "€"), - PriceGross: domain.NewFromInt(7, 1, "€"), + PriceGrossWithDiscounts: domain.NewFromInt(7, 1, "€"), }, }, }, @@ -141,17 +138,16 @@ func Test_CanBuildSimpleSelectionWithGiftCardFullPayment(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(199, 100, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(199, 100, "€"), }, { - ID: "2", - RowPriceGross: domain.NewFromInt(299, 100, "€"), + ID: "2", + RowPriceGrossWithDiscount: domain.NewFromInt(299, 100, "€"), }, }, ShippingItem: ShippingItem{ - PriceNet: domain.NewFromInt(7, 1, "€"), - PriceGross: domain.NewFromInt(7, 1, "€"), + PriceGrossWithDiscounts: domain.NewFromInt(7, 1, "€"), }, }, }, @@ -179,12 +175,12 @@ func Test_CanCalculateGiftCardChargeWithRest(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(4, 1, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(4, 1, "€"), }, { - ID: "2", - RowPriceGross: domain.NewFromInt(8, 1, "€"), + ID: "2", + RowPriceGrossWithDiscount: domain.NewFromInt(8, 1, "€"), }, }, }, @@ -222,12 +218,12 @@ func Test_PayCompleteCartWithGiftCards(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(4, 1, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(4, 1, "€"), }, { - ID: "2", - RowPriceGross: domain.NewFromInt(8, 1, "€"), + ID: "2", + RowPriceGrossWithDiscount: domain.NewFromInt(8, 1, "€"), }, }, }, @@ -239,7 +235,7 @@ func Test_PayCompleteCartWithGiftCards(t *testing.T) { }, } selection, err := NewDefaultPaymentSelection("gateyway", getPaymentMethodMapping(t), cart) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, domain.NewFromInt(12, 1, "€").FloatAmount(), selection.CartSplit().ChargesByType().GetByTypeForced(domain.ChargeTypeGiftCard).Value.FloatAmount()) assert.Equal(t, domain.NewFromInt(0, 1, "€").FloatAmount(), selection.CartSplit().ChargesByType().GetByTypeForced(domain.ChargeTypeMain).Value.FloatAmount()) // item 1 is completely paid for @@ -259,14 +255,13 @@ func Test_CartWithExpensiveItems(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(300099, 100, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(300099, 100, "€"), }, }, ShippingItem: ShippingItem{ - Title: "1", - PriceNet: domain.NewFromInt(88895, 100, "€"), - PriceGross: domain.NewFromInt(88895, 100, "€"), + Title: "1", + PriceGrossWithDiscounts: domain.NewFromInt(88895, 100, "€"), }, }, }, @@ -319,14 +314,13 @@ func Test_CartWithShipping(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(150, 1, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(150, 1, "€"), }, }, ShippingItem: ShippingItem{ - Title: "1", - PriceNet: domain.NewFromInt(99, 1, "€"), - PriceGross: domain.NewFromInt(99, 1, "€"), + Title: "1", + PriceGrossWithDiscounts: domain.NewFromInt(99, 1, "€"), }, }, }, @@ -342,7 +336,7 @@ func Test_CartWithShipping(t *testing.T) { }, } selection, err := NewDefaultPaymentSelection("gateyway", getPaymentMethodMapping(t), cart) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, domain.NewFromInt(160, 1, "€").FloatAmount(), selection.CartSplit().ChargesByType().GetByTypeForced(domain.ChargeTypeGiftCard).Value.FloatAmount()) assert.Equal(t, domain.NewFromInt(89, 1, "€").FloatAmount(), selection.CartSplit().ChargesByType().GetByTypeForced(domain.ChargeTypeMain).Value.FloatAmount()) @@ -382,14 +376,13 @@ func Test_CreateSimplePaymentWithoutGiftCards(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(50, 100, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(50, 100, "€"), }, }, ShippingItem: ShippingItem{ - Title: "1", - PriceNet: domain.NewFromInt(20, 100, "€"), - PriceGross: domain.NewFromInt(20, 100, "€"), + Title: "1", + PriceGrossWithDiscounts: domain.NewFromInt(20, 100, "€"), }, }, }, @@ -417,18 +410,17 @@ func Test_CreatePaymentWithFilteredCharges(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(50, 1, "€"), + ID: "1", + RowPriceGrossWithDiscount: domain.NewFromInt(50, 1, "€"), }, { - ID: "2", - RowPriceGross: domain.NewFromInt(20, 1, "€"), + ID: "2", + RowPriceGrossWithDiscount: domain.NewFromInt(20, 1, "€"), }, }, ShippingItem: ShippingItem{ - Title: "1", - PriceNet: domain.NewFromInt(20, 1, "€"), - PriceGross: domain.NewFromInt(20, 1, "€"), + Title: "1", + PriceGrossWithDiscounts: domain.NewFromInt(20, 1, "€"), }, }, }, @@ -451,7 +443,7 @@ func Test_CreatePaymentWithFilteredCharges(t *testing.T) { }, } selection, err := NewDefaultPaymentSelection("gateyway", getPaymentMethodMapping(t), cart) - assert.NoError(t, err) + require.NoError(t, err) // force type for zero charges assert.Equal(t, domain.NewFromInt(95, 1, "€").FloatAmount(), selection.CartSplit().ChargesByType().GetByTypeForced(domain.ChargeTypeGiftCard).Value.FloatAmount()) assert.Equal(t, domain.NewFromInt(5, 1, "€").FloatAmount(), selection.CartSplit().ChargesByType().GetByTypeForced(domain.ChargeTypeMain).Value.FloatAmount()) @@ -486,8 +478,9 @@ func Test_CreatePaymentWithDiscounts(t *testing.T) { }, Cartitems: []Item{ { - ID: "1", - RowPriceGross: domain.NewFromInt(9995, 100, "€"), + ID: "1", + RowPriceGross: domain.NewFromInt(9995, 100, "€"), + RowPriceGrossWithDiscount: domain.NewFromInt(9995-4998, 100, "€"), AppliedDiscounts: AppliedDiscounts{ { CampaignCode: "campaign-1", @@ -499,9 +492,8 @@ func Test_CreatePaymentWithDiscounts(t *testing.T) { }, }, ShippingItem: ShippingItem{ - Title: "home", - PriceNet: domain.NewFromInt(28, 1, "€"), - PriceGross: domain.NewFromInt(28, 1, "€"), + Title: "home", + PriceGrossWithDiscounts: domain.NewFromInt(28, 1, "€"), }, }, }, diff --git a/cart/domain/decorator/cartDecorator.go b/cart/domain/decorator/cartDecorator.go index 9f2eeac3a..5f033eae3 100644 --- a/cart/domain/decorator/cartDecorator.go +++ b/cart/domain/decorator/cartDecorator.go @@ -177,7 +177,7 @@ func (dci DecoratedCartItem) GetVariantsVariationAttributeCodes() []string { // GetChargesToPay getter func (dci DecoratedCartItem) GetChargesToPay(wishedToPaySum *domain.WishedToPay) priceDomain.Charges { - priceToPayForItem := dci.Item.RowPriceGrossWithDiscount() + priceToPayForItem := dci.Item.RowPriceGrossWithDiscount return dci.Product.SaleableData().GetLoyaltyChargeSplit(&priceToPayForItem, wishedToPaySum, dci.Item.Qty) } diff --git a/cart/domain/decorator/discount_test.go b/cart/domain/decorator/discount_test.go index e832fe491..503ebb07d 100644 --- a/cart/domain/decorator/discount_test.go +++ b/cart/domain/decorator/discount_test.go @@ -79,15 +79,13 @@ func TestDecoratedItem_HasDiscounts(t *testing.T) { { name: "multiple discounts on item", item: func() *decorator.DecoratedCartItem { - builder := cart.ItemBuilder{} - builder.AddDiscount(cart.AppliedDiscount{ + item := cart.Item{ID: "item-1", AppliedDiscounts: []cart.AppliedDiscount{{ CampaignCode: "code-1", Label: "title-1", Type: "type-1", - }) - item, _ := builder.Build() + }}} decorated := decorator.DecoratedCartItem{ - Item: *item, + Item: item, } return &decorated }(), @@ -119,13 +117,9 @@ func TestDecoratedDelivery_MergeDiscounts(t *testing.T) { { name: "delivery with items but without discounts", delivery: func() *decorator.DecoratedDelivery { - builder := cart.DeliveryBuilder{} - builder.AddItem(cart.Item{}) - builder.AddItem(cart.Item{}) - builder.SetDeliveryCode("code") - delivery, _ := builder.Build() + delivery := cart.Delivery{DeliveryInfo: cart.DeliveryInfo{Code: "code"}, Cartitems: []cart.Item{{}, {}}} decorated := decorator.DecoratedDelivery{ - Delivery: *delivery, + Delivery: delivery, } return &decorated }(), @@ -221,40 +215,14 @@ func TestDecoratedCart_MergeDiscounts(t *testing.T) { }, want: cart.AppliedDiscounts{}, }, - { - name: "cart with deliveries but without items", - cart: &decorator.DecoratedCart{ - Cart: cart.Cart{ - Deliveries: func() []cart.Delivery { - result := make([]cart.Delivery, 0) - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - delivery, _ := builder.Build() - result = append(result, *delivery) - builder = cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - result = append(result, *delivery) - return result - }(), - }, - }, - want: cart.AppliedDiscounts{}, - }, { name: "cart with deliveries with items but without discounts", cart: &decorator.DecoratedCart{ Cart: cart.Cart{ Deliveries: func() []cart.Delivery { result := make([]cart.Delivery, 0) - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - builder.AddItem(cart.Item{}) - builder.AddItem(cart.Item{}) - delivery, _ := builder.Build() - result = append(result, *delivery) - builder = cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - result = append(result, *delivery) + delivery := cart.Delivery{DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}} + result = append(result, delivery) return result }(), }, @@ -323,46 +291,6 @@ func TestDecoratedCart_HasDiscounts(t *testing.T) { }, want: false, }, - { - name: "cart with deliveries but without items", - cart: &decorator.DecoratedCart{ - Cart: cart.Cart{ - Deliveries: func() []cart.Delivery { - result := make([]cart.Delivery, 0) - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - delivery, _ := builder.Build() - result = append(result, *delivery) - builder = cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - result = append(result, *delivery) - return result - }(), - }, - }, - want: false, - }, - { - name: "cart with deliveries with items but without discounts", - cart: &decorator.DecoratedCart{ - Cart: cart.Cart{ - Deliveries: func() []cart.Delivery { - result := make([]cart.Delivery, 0) - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - builder.AddItem(cart.Item{}) - builder.AddItem(cart.Item{}) - delivery, _ := builder.Build() - result = append(result, *delivery) - builder = cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - result = append(result, *delivery) - return result - }(), - }, - }, - want: false, - }, { name: "cart with deliveries with items with discounts", cart: &decorator.DecoratedCart{ diff --git a/cart/domain/decorator/giftcard.go b/cart/domain/decorator/giftcard.go index 566695fd2..ae382b340 100644 --- a/cart/domain/decorator/giftcard.go +++ b/cart/domain/decorator/giftcard.go @@ -2,10 +2,6 @@ package decorator import "flamingo.me/flamingo-commerce/v3/price/domain" -const ( - decoratedGiftCardError = "Unable to collect discounts, stopping and returning empty slice" -) - type ( // DecoratedWithGiftCard interface for a decorated object to be able to handle giftcards // the difference to cart.WithGiftCard is, that these functions do NOT provide the client @@ -13,8 +9,8 @@ type ( DecoratedWithGiftCard interface { HasRemainingGiftCards() bool HasAppliedGiftCards() bool - SumAppliedGiftCards() domain.Price - SumGrandTotalWithGiftCards() domain.Price + TotalGiftCardAmount() domain.Price + GrandTotalWithGiftCards() domain.Price } ) @@ -33,22 +29,13 @@ func (dc DecoratedCart) HasAppliedGiftCards() bool { return dc.Cart.HasAppliedGiftCards() } -// SumAppliedGiftCards sum up all applied amounts of giftcads +// TotalGiftCardAmount sum up all applied amounts of giftcads // price is returned as a payable -func (dc DecoratedCart) SumAppliedGiftCards() domain.Price { - return dc.executeAndLog(dc.Cart.SumAppliedGiftCards) +func (dc DecoratedCart) TotalGiftCardAmount() domain.Price { + return dc.Cart.TotalGiftCardAmount } -// SumGrandTotalWithGiftCards calculate the grand total of the cart minus gift cards -func (dc DecoratedCart) SumGrandTotalWithGiftCards() domain.Price { - return dc.executeAndLog(dc.Cart.SumGrandTotalWithGiftCards) -} - -// executeAndLog executes given function and logs in case of an error -func (dc DecoratedCart) executeAndLog(toExecute func() (domain.Price, error)) domain.Price { - result, err := toExecute() - if err != nil { - dc.Logger.Error(decoratedGiftCardError) - } - return result +// GrandTotalWithGiftCards calculate the grand total of the cart minus gift cards +func (dc DecoratedCart) GrandTotalWithGiftCards() domain.Price { + return dc.Cart.GrandTotalWithGiftCards } diff --git a/cart/domain/testutils/utils.go b/cart/domain/testutils/utils.go index e9b8cb568..5eca5919b 100644 --- a/cart/domain/testutils/utils.go +++ b/cart/domain/testutils/utils.go @@ -7,115 +7,98 @@ import ( "flamingo.me/flamingo-commerce/v3/price/domain" ) -type ( - // ByCode implements sort.Interface for []AppliedDiscount based on code - ByCode cart.AppliedDiscounts -) - -// implementations for sort interface - -func (a ByCode) Len() int { - return len(a) -} - -func (a ByCode) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a ByCode) Less(i, j int) bool { - return a[i].CampaignCode < a[j].CampaignCode -} - // BuildItemWithDiscounts helper for item building func BuildItemWithDiscounts(t *testing.T) *cart.Item { t.Helper() - builder := cart.ItemBuilder{} - builder.AddDiscount(cart.AppliedDiscount{ - CampaignCode: "code-1", - Label: "title-1", - Type: "type-1", - Applied: domain.NewFromFloat(-10.0, "$"), - SortOrder: 3, - }) - builder.AddDiscount(cart.AppliedDiscount{ - CampaignCode: "code-2", - Label: "title-2", - Type: "type-1", - Applied: domain.NewFromFloat(-15.0, "$"), - SortOrder: 2, - }) - builder.AddDiscount(cart.AppliedDiscount{ - CampaignCode: "code-3", - Label: "title-1", - Type: "type-2", - Applied: domain.NewFromFloat(-5.0, "$"), - SortOrder: 4, - }) - builder.SetID("id-1") - item, err := builder.Build() - if err != nil { - t.Fatalf("Could not build item %s", err.Error()) + item := cart.Item{ID: "id-1", + AppliedDiscounts: []cart.AppliedDiscount{ + { + CampaignCode: "code-1", + Label: "title-1", + Type: "type-1", + Applied: domain.NewFromFloat(-10.0, "$"), + SortOrder: 3, + }, + { + CampaignCode: "code-2", + Label: "title-2", + Type: "type-1", + Applied: domain.NewFromFloat(-15.0, "$"), + SortOrder: 2, + }, + { + CampaignCode: "code-3", + Label: "title-1", + Type: "type-2", + Applied: domain.NewFromFloat(-5.0, "$"), + SortOrder: 4, + }, + }, } - return item + + // todo: add discount total + + return &item } // BuildItemWithAlternativeDiscounts helper for item building with different discounts func BuildItemWithAlternativeDiscounts(t *testing.T) *cart.Item { t.Helper() - builder := cart.ItemBuilder{} - builder.AddDiscount(cart.AppliedDiscount{ - CampaignCode: "code-4", - Label: "title-4", - Type: "type-1", - Applied: domain.NewFromFloat(-10.0, "$"), - SortOrder: 5, - }) - builder.AddDiscount(cart.AppliedDiscount{ - CampaignCode: "code-5", - Label: "title-5", - Type: "type-1", - Applied: domain.NewFromFloat(-15.0, "$"), - SortOrder: 0, - }) - builder.AddDiscount(cart.AppliedDiscount{ - CampaignCode: "code-6", - Label: "title-6", - Type: "type-2", - Applied: domain.NewFromFloat(-5.0, "$"), - SortOrder: 1, - }) - builder.SetID("id-2") - item, err := builder.Build() - if err != nil { - t.Fatalf("Could not build item %s", err.Error()) - } - return item + item := cart.Item{ + ID: "id-2", + AppliedDiscounts: []cart.AppliedDiscount{ + cart.AppliedDiscount{ + CampaignCode: "code-4", + Label: "title-4", + Type: "type-1", + Applied: domain.NewFromFloat(-10.0, "$"), + SortOrder: 5, + }, + cart.AppliedDiscount{ + CampaignCode: "code-5", + Label: "title-5", + Type: "type-1", + Applied: domain.NewFromFloat(-15.0, "$"), + SortOrder: 0, + }, + cart.AppliedDiscount{ + CampaignCode: "code-6", + Label: "title-6", + Type: "type-2", + Applied: domain.NewFromFloat(-5.0, "$"), + SortOrder: 1, + }, + }, + } // todo: add discount total + + return &item } // BuildItemWithDuplicateDiscounts helper for item building with duplicate discounts func BuildItemWithDuplicateDiscounts(t *testing.T) *cart.Item { t.Helper() - builder := cart.ItemBuilder{} - builder.AddDiscount(cart.AppliedDiscount{ - CampaignCode: "code-1", - Label: "title-1", - Type: "type-1", - Applied: domain.NewFromFloat(-10.0, "$"), - SortOrder: 0, - }) - builder.AddDiscount(cart.AppliedDiscount{ - CampaignCode: "code-1", - Label: "title-1", - Type: "type-1", - Applied: domain.NewFromFloat(-10.0, "$"), - SortOrder: 0, - }) - builder.SetID("id-1") - item, err := builder.Build() - if err != nil { - t.Fatalf("Could not build item %s", err.Error()) - } - return item + + item := cart.Item{ + ID: "id-1", + AppliedDiscounts: []cart.AppliedDiscount{ + cart.AppliedDiscount{ + CampaignCode: "code-1", + Label: "title-1", + Type: "type-1", + Applied: domain.NewFromFloat(-10.0, "$"), + SortOrder: 0, + }, + cart.AppliedDiscount{ + CampaignCode: "code-1", + Label: "title-1", + Type: "type-1", + Applied: domain.NewFromFloat(-10.0, "$"), + SortOrder: 0, + }, + }, + } // todo: add discount total + + return &item } // BuildShippingItemWithDiscounts helper for shipping item building @@ -205,14 +188,9 @@ func BuildShippingItemWithDuplicateDiscounts(t *testing.T) *cart.ShippingItem { // The amount should be added to the previous discount func BuildDeliveryWithDiscounts(t *testing.T) *cart.Delivery { t.Helper() - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code") - builder.AddItem(*BuildItemWithDiscounts(t)) - builder.AddItem(*BuildItemWithDiscounts(t)) - // add items with discounts - delivery, err := builder.Build() - if err != nil { - t.Fatalf("Could not build delivery %s", err.Error()) + delivery := &cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code"}, + Cartitems: []cart.Item{*BuildItemWithDiscounts(t), *BuildItemWithDiscounts(t)}, } return delivery } @@ -223,14 +201,9 @@ func BuildDeliveryWithDiscounts(t *testing.T) *cart.Delivery { // The amount should be added to the previous discount func BuildAlternativeDeliveryWithAlternativeDiscounts(t *testing.T) *cart.Delivery { t.Helper() - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-2") - builder.AddItem(*BuildItemWithAlternativeDiscounts(t)) - builder.AddItem(*BuildItemWithAlternativeDiscounts(t)) - // add items with discounts - delivery, err := builder.Build() - if err != nil { - t.Fatalf("Could not build delivery %s", err.Error()) + delivery := &cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code-2"}, + Cartitems: []cart.Item{*BuildItemWithAlternativeDiscounts(t), *BuildItemWithAlternativeDiscounts(t)}, } return delivery } @@ -241,14 +214,9 @@ func BuildAlternativeDeliveryWithAlternativeDiscounts(t *testing.T) *cart.Delive // The amount should be added to the previous discount func BuildDeliveryWithDifferentDiscounts(t *testing.T) *cart.Delivery { t.Helper() - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - builder.AddItem(*BuildItemWithDiscounts(t)) - builder.AddItem(*BuildItemWithAlternativeDiscounts(t)) - // add items with discounts - delivery, err := builder.Build() - if err != nil { - t.Fatalf("Could not build delivery %s", err.Error()) + delivery := &cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}, + Cartitems: []cart.Item{*BuildItemWithDiscounts(t), *BuildItemWithAlternativeDiscounts(t)}, } return delivery } @@ -259,13 +227,9 @@ func BuildDeliveryWithDifferentDiscounts(t *testing.T) *cart.Delivery { // The amount should be added to the previous discount func BuildDeliveryWithDuplicateDiscounts(t *testing.T) *cart.Delivery { t.Helper() - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - builder.AddItem(*BuildItemWithDuplicateDiscounts(t)) - // add items with discounts - delivery, err := builder.Build() - if err != nil { - t.Fatalf("Could not build delivery %s", err.Error()) + delivery := &cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}, + Cartitems: []cart.Item{*BuildItemWithDuplicateDiscounts(t)}, } return delivery } @@ -273,26 +237,9 @@ func BuildDeliveryWithDuplicateDiscounts(t *testing.T) *cart.Delivery { // BuildDeliveryWithoutDiscounts helper for delivery building func BuildDeliveryWithoutDiscounts(t *testing.T) *cart.Delivery { t.Helper() - builder := cart.DeliveryBuilder{} - builder.AddItem(cart.Item{}) - builder.AddItem(cart.Item{}) - builder.SetDeliveryCode("code") - delivery, err := builder.Build() - if err != nil { - t.Fatalf("Could not build delivery %s", err.Error()) - } - return delivery -} - -// BuildDeliveryWithoutItemsButWithShippingDiscounts helper for delivery building -func BuildDeliveryWithoutItemsButWithShippingDiscounts(t *testing.T) *cart.Delivery { - t.Helper() - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code") - builder.SetShippingItem(*BuildShippingItemWithDiscounts(t)) - delivery, err := builder.Build() - if err != nil { - t.Fatalf("Could not build delivery %s", err.Error()) + delivery := &cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code"}, + Cartitems: []cart.Item{{}, {}}, } return delivery } @@ -300,15 +247,13 @@ func BuildDeliveryWithoutItemsButWithShippingDiscounts(t *testing.T) *cart.Deliv // BuildDeliveryWithoutDiscountsAndShippingDiscounts helper for delivery building func BuildDeliveryWithoutDiscountsAndShippingDiscounts(t *testing.T) *cart.Delivery { t.Helper() - builder := cart.DeliveryBuilder{} - builder.AddItem(cart.Item{}) - builder.AddItem(cart.Item{}) - builder.SetDeliveryCode("code") - builder.SetShippingItem(*BuildShippingItemWithDiscounts(t)) - delivery, err := builder.Build() - if err != nil { - t.Fatalf("Could not build delivery %s", err.Error()) + + delivery := &cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code"}, + Cartitems: []cart.Item{{}, {}}, + ShippingItem: *BuildShippingItemWithDiscounts(t), } + return delivery } @@ -319,16 +264,12 @@ func BuildDeliveryWithoutDiscountsAndShippingDiscounts(t *testing.T) *cart.Deliv // The amount should be added to the previous discount func BuildDeliveryWithDifferentDiscountsAndShippingDiscounts(t *testing.T) *cart.Delivery { t.Helper() - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - builder.AddItem(*BuildItemWithDiscounts(t)) - builder.AddItem(*BuildItemWithAlternativeDiscounts(t)) - builder.SetShippingItem(*BuildShippingItemWithDiscounts(t)) - // add items with discounts - delivery, err := builder.Build() - if err != nil { - t.Fatalf("Could not build delivery %s", err.Error()) + delivery := &cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}, + Cartitems: []cart.Item{*BuildItemWithDiscounts(t), *BuildItemWithAlternativeDiscounts(t)}, + ShippingItem: *BuildShippingItemWithDiscounts(t), } + return delivery } @@ -339,14 +280,11 @@ func BuildDeliveryWithDifferentDiscountsAndShippingDiscounts(t *testing.T) *cart // The amount should be added to the previous discount func BuildDeliveryWithDuplicateDiscountsAndShippingDiscounts(t *testing.T) *cart.Delivery { t.Helper() - builder := cart.DeliveryBuilder{} - builder.SetDeliveryCode("code-1") - builder.AddItem(*BuildItemWithDuplicateDiscounts(t)) - builder.SetShippingItem(*BuildShippingItemWithDiscounts(t)) - // add items with discounts - delivery, err := builder.Build() - if err != nil { - t.Fatalf("Could not build delivery %s", err.Error()) + delivery := &cart.Delivery{ + DeliveryInfo: cart.DeliveryInfo{Code: "code-1"}, + Cartitems: []cart.Item{*BuildItemWithDuplicateDiscounts(t)}, + ShippingItem: *BuildShippingItemWithDiscounts(t), } + return delivery } diff --git a/cart/infrastructure/defaultCartBehaviour.go b/cart/infrastructure/defaultCartBehaviour.go index 00fc2d320..9e7024e63 100644 --- a/cart/infrastructure/defaultCartBehaviour.go +++ b/cart/infrastructure/defaultCartBehaviour.go @@ -18,15 +18,14 @@ import ( type ( // DefaultCartBehaviour defines the in memory cart order behaviour DefaultCartBehaviour struct { - cartStorage CartStorage - productService domain.ProductService - logger flamingo.Logger - itemBuilderProvider domaincart.ItemBuilderProvider - deliveryBuilderProvider domaincart.DeliveryBuilderProvider - cartBuilderProvider domaincart.BuilderProvider - giftCardHandler GiftCardHandler - voucherHandler VoucherHandler - defaultTaxRate float64 + cartStorage CartStorage + productService domain.ProductService + logger flamingo.Logger + giftCardHandler GiftCardHandler + voucherHandler VoucherHandler + defaultTaxRate float64 + grossPricing bool + defaultCurrency string } // CartStorage Interface - might be implemented by other persistence types later as well @@ -69,25 +68,25 @@ func (cob *DefaultCartBehaviour) Inject( CartStorage CartStorage, ProductService domain.ProductService, Logger flamingo.Logger, - itemBuilderProvider domaincart.ItemBuilderProvider, - deliveryBuilderProvider domaincart.DeliveryBuilderProvider, - cartBuilderProvider domaincart.BuilderProvider, voucherHandler VoucherHandler, giftCardHandler GiftCardHandler, config *struct { - DefaultTaxRate float64 `inject:"config:commerce.cart.defaultCartAdapter.defaultTaxRate,optional"` + DefaultTaxRate float64 `inject:"config:commerce.cart.defaultCartAdapter.defaultTaxRate,optional"` + ProductPricing string `inject:"config:commerce.cart.defaultCartAdapter.productPrices"` + DefaultCurrency string `inject:"config:commerce.cart.defaultCartAdapter.defaultCurrency"` }, ) { cob.cartStorage = CartStorage cob.productService = ProductService cob.logger = Logger.WithField(flamingo.LogKeyCategory, "inmemorybehaviour") - cob.itemBuilderProvider = itemBuilderProvider - cob.deliveryBuilderProvider = deliveryBuilderProvider - cob.cartBuilderProvider = cartBuilderProvider cob.voucherHandler = voucherHandler cob.giftCardHandler = giftCardHandler if config != nil { cob.defaultTaxRate = config.DefaultTaxRate + cob.defaultCurrency = config.DefaultCurrency + if config.ProductPricing == "gross" { + cob.grossPricing = true + } } } @@ -106,6 +105,7 @@ func (cob *DefaultCartBehaviour) Restore(ctx context.Context, cart *domaincart.C if err != nil { return nil, nil, err } + cob.collectTotals(cart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, err @@ -144,6 +144,7 @@ func (cob *DefaultCartBehaviour) DeleteItem(ctx context.Context, cart *domaincar } } + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "newCart.infrastructure.DefaultCartBehaviour: error on saving newCart") @@ -167,6 +168,7 @@ func (cob *DefaultCartBehaviour) UpdateItem(ctx context.Context, cart *domaincar return nil, nil, err } + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -193,6 +195,7 @@ func (cob *DefaultCartBehaviour) UpdateItems(ctx context.Context, cart *domainca } } + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -202,33 +205,43 @@ func (cob *DefaultCartBehaviour) UpdateItems(ctx context.Context, cart *domainca } func (cob *DefaultCartBehaviour) updateItem(ctx context.Context, cart *domaincart.Cart, itemUpdateCommand domaincart.ItemUpdateCommand) error { - itemBuilder := cob.itemBuilderProvider() itemDelivery, err := cart.GetDeliveryByItemID(itemUpdateCommand.ItemID) if err != nil { return err } cob.logger.WithContext(ctx).Info("Inmemory Service Update %v in %#v", itemUpdateCommand.ItemID, itemDelivery.Cartitems) - for _, item := range itemDelivery.Cartitems { + for k, item := range itemDelivery.Cartitems { if itemUpdateCommand.ItemID == item.ID { - itemBuilder.SetFromItem(item) if itemUpdateCommand.Qty != nil { - itemBuilder.SetQty(*itemUpdateCommand.Qty) + itemDelivery.Cartitems[k].Qty = *itemUpdateCommand.Qty + + gross := item.SinglePriceGross.Clone().Amount().Mul(item.SinglePriceGross.Amount(), big.NewFloat(float64(*itemUpdateCommand.Qty))) + itemDelivery.Cartitems[k].RowPriceGross = priceDomain.NewFromBigFloat(*gross, item.SinglePriceGross.Currency()) + + net := item.SinglePriceNet.Clone().Amount().Mul(item.SinglePriceNet.Amount(), big.NewFloat(float64(*itemUpdateCommand.Qty))) + itemDelivery.Cartitems[k].RowPriceNet = priceDomain.NewFromBigFloat(*net, item.SinglePriceNet.Currency()) + + itemDelivery.Cartitems[k].RowPriceGrossWithDiscount = itemDelivery.Cartitems[k].RowPriceGross + itemDelivery.Cartitems[k].RowPriceNetWithDiscount = itemDelivery.Cartitems[k].RowPriceNet + + itemDelivery.Cartitems[k].RowPriceGrossWithItemRelatedDiscount = itemDelivery.Cartitems[k].RowPriceGross + itemDelivery.Cartitems[k].RowPriceNetWithItemRelatedDiscount = itemDelivery.Cartitems[k].RowPriceNet + + if cob.defaultTaxRate > 0.0 { + taxAmount, err := itemDelivery.Cartitems[k].RowPriceGross.Sub(itemDelivery.Cartitems[k].RowPriceNet) + if err != nil { + return err + } + itemDelivery.Cartitems[k].RowTaxes[0].Amount = taxAmount + } + } if itemUpdateCommand.SourceID != nil { - itemBuilder.SetSourceID(*itemUpdateCommand.SourceID) - } - itemBuilder.AddTaxInfo("default", big.NewFloat(cob.defaultTaxRate), nil).CalculatePricesAndTax() - newItem, err := itemBuilder.Build() - if err != nil { - return err - } - for k, currentItem := range itemDelivery.Cartitems { - if currentItem.ID == itemUpdateCommand.ItemID { - itemDelivery.Cartitems[k] = *newItem - } + itemDelivery.Cartitems[k].SourceID = *itemUpdateCommand.SourceID } + } } @@ -290,6 +303,7 @@ func (cob *DefaultCartBehaviour) AddToCart(ctx context.Context, cart *domaincart } } + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -299,8 +313,6 @@ func (cob *DefaultCartBehaviour) AddToCart(ctx context.Context, cart *domaincart } func (cob *DefaultCartBehaviour) buildItemForCart(ctx context.Context, addRequest domaincart.AddRequest) (*domaincart.Item, error) { - itemBuilder := cob.itemBuilderProvider() - // create and add new item product, err := cob.productService.Get(ctx, addRequest.MarketplaceCode) if err != nil { @@ -316,15 +328,57 @@ func (cob *DefaultCartBehaviour) buildItemForCart(ctx context.Context, addReques product = productWithActiveVariant } - itemBuilder. - SetQty(addRequest.Qty). - AddTaxInfo("default", big.NewFloat(cob.defaultTaxRate), nil). - SetByProduct(product). - SetID(strconv.Itoa(rand.Int())). - SetExternalReference(strconv.Itoa(rand.Int())). - SetAdditionalData(addRequest.AdditionalData) + return cob.createCartItemFromProduct(addRequest.Qty, addRequest.MarketplaceCode, addRequest.VariantMarketplaceCode, addRequest.AdditionalData, product) +} +func (cob *DefaultCartBehaviour) createCartItemFromProduct(qty int, marketplaceCode string, variantMarketPlaceCode string, additonalData map[string]string, product domain.BasicProduct) (*domaincart.Item, error) { + item := &domaincart.Item{ + ID: strconv.Itoa(rand.Int()), + ExternalReference: strconv.Itoa(rand.Int()), + MarketplaceCode: marketplaceCode, + VariantMarketPlaceCode: variantMarketPlaceCode, + ProductName: product.BaseData().Title, + Qty: qty, + AdditionalData: additonalData, + } + + currency := product.SaleableData().ActivePrice.GetFinalPrice().Currency() + + if cob.grossPricing { + item.SinglePriceGross = product.SaleableData().ActivePrice.GetFinalPrice().GetPayable() + net := item.SinglePriceGross.Clone().Amount().Quo(item.SinglePriceGross.Amount(), big.NewFloat(1+(cob.defaultTaxRate/100))) + item.SinglePriceNet = priceDomain.NewFromBigFloat(*net, currency).GetPayable() + } else { + item.SinglePriceNet = product.SaleableData().ActivePrice.GetFinalPrice().GetPayable() + gross := item.SinglePriceGross.Clone().Amount().Mul(item.SinglePriceNet.Amount(), big.NewFloat(1+(cob.defaultTaxRate/100))) + item.SinglePriceGross = priceDomain.NewFromBigFloat(*gross, currency).GetPayable() + } + + gross := item.SinglePriceGross.Clone().Amount().Mul(item.SinglePriceGross.Amount(), big.NewFloat(float64(qty))) + item.RowPriceGross = priceDomain.NewFromBigFloat(*gross, currency) + _ = item.RowPriceGross.FloatAmount() + net := item.SinglePriceNet.Clone().Amount().Mul(item.SinglePriceNet.Amount(), big.NewFloat(float64(qty))) + item.RowPriceNet = priceDomain.NewFromBigFloat(*net, currency) + _ = item.RowPriceNet.FloatAmount() + + item.RowPriceGrossWithDiscount, item.RowPriceNetWithDiscount = item.RowPriceGross, item.RowPriceNet + item.RowPriceGrossWithItemRelatedDiscount, item.RowPriceNetWithItemRelatedDiscount = item.RowPriceGross, item.RowPriceNet + if cob.defaultTaxRate > 0.0 { + taxAmount, err := item.RowPriceGross.Sub(item.RowPriceNet) + if err != nil { + return nil, err + } + item.RowTaxes = []domaincart.Tax{{ + Amount: taxAmount, + Type: "default", + Rate: big.NewFloat(cob.defaultTaxRate), + }} + } + + item.TotalDiscountAmount = priceDomain.NewZero(currency) + item.ItemRelatedDiscountAmount = priceDomain.NewZero(currency) + item.NonItemRelatedDiscountAmount = priceDomain.NewZero(currency) - return itemBuilder.Build() + return item, nil } // CleanCart removes everything from the cart, e.g. deliveries, billing address, etc @@ -347,6 +401,7 @@ func (cob *DefaultCartBehaviour) CleanCart(ctx context.Context, cart *domaincart newCart.BillingAddress = nil newCart.Totalitems = nil + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -384,6 +439,7 @@ func (cob *DefaultCartBehaviour) CleanDelivery(ctx context.Context, cart *domain newCart.Deliveries[newLength] = domaincart.Delivery{} newCart.Deliveries = newCart.Deliveries[:newLength] + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -405,6 +461,7 @@ func (cob *DefaultCartBehaviour) UpdatePurchaser(ctx context.Context, cart *doma newCart.AdditionalData.CustomAttributes = additionalData.CustomAttributes } + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -422,6 +479,7 @@ func (cob *DefaultCartBehaviour) UpdateBillingAddress(ctx context.Context, cart newCart.BillingAddress = &billingAddress + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -438,6 +496,7 @@ func (cob *DefaultCartBehaviour) UpdateAdditionalData(ctx context.Context, cart } newCart.AdditionalData = *additionalData + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { @@ -462,6 +521,7 @@ func (cob *DefaultCartBehaviour) UpdatePaymentSelection(ctx context.Context, car } newCart.PaymentSelection = paymentSelection + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -483,6 +543,7 @@ func (cob *DefaultCartBehaviour) UpdateDeliveryInfo(ctx context.Context, cart *d for key, delivery := range newCart.Deliveries { if delivery.DeliveryInfo.Code == deliveryCode { newCart.Deliveries[key].DeliveryInfo = deliveryInfo + cob.collectTotals(&newCart) err := cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -492,6 +553,7 @@ func (cob *DefaultCartBehaviour) UpdateDeliveryInfo(ctx context.Context, cart *d } newCart.Deliveries = append(newCart.Deliveries, domaincart.Delivery{DeliveryInfo: deliveryInfo}) + cob.collectTotals(&newCart) err = cob.cartStorage.StoreCart(ctx, &newCart) if err != nil { return nil, nil, errors.Wrap(err, "cart.infrastructure.DefaultCartBehaviour: error on saving cart") @@ -532,6 +594,8 @@ func (cob *DefaultCartBehaviour) StoreNewCart(ctx context.Context, newCart *doma if newCart.ID == "" { return nil, errors.New("no id given") } + newCart.DefaultCurrency = cob.defaultCurrency + cob.collectTotals(newCart) return newCart, cob.cartStorage.StoreCart(ctx, newCart) } @@ -547,6 +611,7 @@ func (cob *DefaultCartBehaviour) ApplyVoucher(ctx context.Context, cart *domainc return nil, nil, err } + cob.collectTotals(newCartWithVoucher) err = cob.cartStorage.StoreCart(ctx, newCartWithVoucher) if err != nil { return nil, nil, err @@ -579,6 +644,7 @@ func (cob *DefaultCartBehaviour) RemoveVoucher(ctx context.Context, cart *domain return nil, nil, err } + cob.collectTotals(newCartWithoutVoucher) err = cob.cartStorage.StoreCart(ctx, newCartWithoutVoucher) if err != nil { return nil, nil, err @@ -600,6 +666,7 @@ func (cob *DefaultCartBehaviour) ApplyGiftCard(ctx context.Context, cart *domain return nil, nil, err } + cob.collectTotals(newCartWithGiftCard) err = cob.cartStorage.StoreCart(ctx, newCartWithGiftCard) if err != nil { return nil, nil, err @@ -620,6 +687,7 @@ func (cob *DefaultCartBehaviour) RemoveGiftCard(ctx context.Context, cart *domai return nil, nil, err } + cob.collectTotals(newCartWithOutGiftCard) err = cob.cartStorage.StoreCart(ctx, newCartWithOutGiftCard) if err != nil { return nil, nil, err @@ -639,7 +707,7 @@ func (cob *DefaultCartBehaviour) checkPaymentSelection(ctx context.Context, cart } paymentSelectionTotal := paymentSelection.TotalValue() - if !cart.GrandTotal().LikelyEqual(paymentSelectionTotal) { + if !cart.GrandTotal.LikelyEqual(paymentSelectionTotal) { return errors.New("Payment Total does not match with Grandtotal") } return nil @@ -660,6 +728,91 @@ func (cob *DefaultCartBehaviour) resetPaymentSelectionIfInvalid(ctx context.Cont return cart, nil, nil } +func (cob *DefaultCartBehaviour) collectTotals(cart *domaincart.Cart) { + cart.TotalGiftCardAmount = priceDomain.NewZero(cart.DefaultCurrency) + cart.GrandTotalWithGiftCards = priceDomain.NewZero(cart.DefaultCurrency) + cart.GrandTotal = priceDomain.NewZero(cart.DefaultCurrency) + cart.GrandTotalNet = priceDomain.NewZero(cart.DefaultCurrency) + cart.GrandTotalNetWithGiftCards = priceDomain.NewZero(cart.DefaultCurrency) + cart.ShippingNet = priceDomain.NewZero(cart.DefaultCurrency) + cart.ShippingNetWithDiscounts = priceDomain.NewZero(cart.DefaultCurrency) + cart.ShippingGross = priceDomain.NewZero(cart.DefaultCurrency) + cart.ShippingGrossWithDiscounts = priceDomain.NewZero(cart.DefaultCurrency) + cart.SubTotalGross = priceDomain.NewZero(cart.DefaultCurrency) + cart.SubTotalNet = priceDomain.NewZero(cart.DefaultCurrency) + cart.SubTotalGrossWithDiscounts = priceDomain.NewZero(cart.DefaultCurrency) + cart.SubTotalNetWithDiscounts = priceDomain.NewZero(cart.DefaultCurrency) + cart.TotalDiscountAmount = priceDomain.NewZero(cart.DefaultCurrency) + cart.NonItemRelatedDiscountAmount = priceDomain.NewZero(cart.DefaultCurrency) + cart.ItemRelatedDiscountAmount = priceDomain.NewZero(cart.DefaultCurrency) + + for i := 0; i < len(cart.Deliveries); i++ { + delivery := &cart.Deliveries[i] + delivery.SubTotalGross = priceDomain.NewZero(cart.DefaultCurrency) + delivery.SubTotalNet = priceDomain.NewZero(cart.DefaultCurrency) + delivery.TotalDiscountAmount = priceDomain.NewZero(cart.DefaultCurrency) + delivery.SubTotalDiscountAmount = priceDomain.NewZero(cart.DefaultCurrency) + delivery.NonItemRelatedDiscountAmount = priceDomain.NewZero(cart.DefaultCurrency) + delivery.ItemRelatedDiscountAmount = priceDomain.NewZero(cart.DefaultCurrency) + delivery.SubTotalGrossWithDiscounts = priceDomain.NewZero(cart.DefaultCurrency) + delivery.SubTotalNetWithDiscounts = priceDomain.NewZero(cart.DefaultCurrency) + delivery.GrandTotal = priceDomain.NewZero(cart.DefaultCurrency) + + if !delivery.ShippingItem.PriceGrossWithDiscounts.IsZero() { + delivery.GrandTotal = delivery.GrandTotal.ForceAdd(delivery.ShippingItem.PriceGrossWithDiscounts) + discounts, _ := delivery.ShippingItem.AppliedDiscounts.Sum() + delivery.TotalDiscountAmount = delivery.TotalDiscountAmount.ForceAdd(discounts) + } + + for _, cartitem := range delivery.Cartitems { + delivery.SubTotalGross = delivery.SubTotalGross.ForceAdd(cartitem.RowPriceGross) + delivery.SubTotalNet = delivery.SubTotalNet.ForceAdd(cartitem.RowPriceNet) + delivery.TotalDiscountAmount = delivery.TotalDiscountAmount.ForceAdd(cartitem.TotalDiscountAmount) + delivery.SubTotalDiscountAmount = delivery.SubTotalDiscountAmount.ForceAdd(cartitem.TotalDiscountAmount) + delivery.NonItemRelatedDiscountAmount = delivery.NonItemRelatedDiscountAmount.ForceAdd(cartitem.NonItemRelatedDiscountAmount) + delivery.ItemRelatedDiscountAmount = delivery.ItemRelatedDiscountAmount.ForceAdd(cartitem.ItemRelatedDiscountAmount) + delivery.SubTotalGrossWithDiscounts = delivery.SubTotalGrossWithDiscounts.ForceAdd(cartitem.RowPriceGrossWithDiscount) + delivery.SubTotalNetWithDiscounts = delivery.SubTotalNetWithDiscounts.ForceAdd(cartitem.RowPriceNetWithDiscount) + delivery.GrandTotal = delivery.GrandTotal.ForceAdd(cartitem.RowPriceGrossWithDiscount) + } + + cart.GrandTotal = cart.GrandTotal.ForceAdd(delivery.GrandTotal) + cart.GrandTotalNet = cart.GrandTotalNet.ForceAdd(delivery.SubTotalNetWithDiscounts).ForceAdd(delivery.ShippingItem.PriceNetWithDiscounts) + cart.ShippingNet = cart.ShippingNet.ForceAdd(delivery.ShippingItem.PriceNet) + cart.ShippingNetWithDiscounts = cart.ShippingNetWithDiscounts.ForceAdd(delivery.ShippingItem.PriceNetWithDiscounts) + cart.ShippingGross = cart.ShippingGross.ForceAdd(delivery.ShippingItem.PriceGross) + cart.ShippingGrossWithDiscounts = cart.ShippingGrossWithDiscounts.ForceAdd(delivery.ShippingItem.PriceGrossWithDiscounts) + cart.SubTotalGross = cart.SubTotalGross.ForceAdd(delivery.SubTotalGross) + cart.SubTotalNet = cart.SubTotalNet.ForceAdd(delivery.SubTotalNet) + cart.SubTotalGrossWithDiscounts = cart.SubTotalGrossWithDiscounts.ForceAdd(delivery.SubTotalGrossWithDiscounts) + cart.SubTotalNetWithDiscounts = cart.SubTotalNetWithDiscounts.ForceAdd(delivery.SubTotalNetWithDiscounts) + cart.TotalDiscountAmount = cart.TotalDiscountAmount.ForceAdd(delivery.TotalDiscountAmount) + cart.NonItemRelatedDiscountAmount = cart.NonItemRelatedDiscountAmount.ForceAdd(delivery.NonItemRelatedDiscountAmount) + cart.ItemRelatedDiscountAmount = cart.ItemRelatedDiscountAmount.ForceAdd(delivery.ItemRelatedDiscountAmount) + } + + for _, totalitem := range cart.Totalitems { + cart.GrandTotal = cart.GrandTotal.ForceAdd(totalitem.Price) + } + + sumAppliedGiftCards := priceDomain.NewZero(cart.DefaultCurrency) + for _, card := range cart.AppliedGiftCards { + sumAppliedGiftCards = sumAppliedGiftCards.ForceAdd(card.Applied) + } + + cart.TotalGiftCardAmount = sumAppliedGiftCards + + cart.GrandTotalWithGiftCards, _ = cart.GrandTotal.Sub(cart.TotalGiftCardAmount) + if cart.GrandTotalWithGiftCards.IsNegative() { + cart.GrandTotalWithGiftCards = priceDomain.NewZero(cart.DefaultCurrency) + } + + cart.GrandTotalNetWithGiftCards, _ = cart.GrandTotalNet.Sub(cart.TotalGiftCardAmount) + if cart.GrandTotalNetWithGiftCards.IsNegative() { + cart.GrandTotalNetWithGiftCards = priceDomain.NewZero(cart.DefaultCurrency) + } +} + // ApplyVoucher checks the voucher and adds the voucher to the supplied cart if valid func (DefaultVoucherHandler) ApplyVoucher(_ context.Context, cart *domaincart.Cart, couponCode string) (*domaincart.Cart, error) { if couponCode != "valid_voucher" && couponCode != "valid" { @@ -696,9 +849,10 @@ func (DefaultGiftCardHandler) ApplyGiftCard(_ context.Context, cart *domaincart. giftCard := domaincart.AppliedGiftCard{ Code: giftCardCode, - Applied: priceDomain.NewFromInt(10, 100, "$"), - Remaining: priceDomain.NewFromInt(0, 100, "$"), + Applied: priceDomain.NewFromInt(10, 100, cart.DefaultCurrency), + Remaining: priceDomain.NewFromInt(0, 100, cart.DefaultCurrency), } + cart.AppliedGiftCards = append(cart.AppliedGiftCards, giftCard) return cart, nil diff --git a/cart/infrastructure/defaultCartBehaviour_test.go b/cart/infrastructure/defaultCartBehaviour_test.go index 6feba49cd..37660db3e 100644 --- a/cart/infrastructure/defaultCartBehaviour_test.go +++ b/cart/infrastructure/defaultCartBehaviour_test.go @@ -2,14 +2,15 @@ package infrastructure import ( "context" - "reflect" "testing" domaincart "flamingo.me/flamingo-commerce/v3/cart/domain/cart" priceDomain "flamingo.me/flamingo-commerce/v3/price/domain" + "flamingo.me/flamingo-commerce/v3/product/domain" "flamingo.me/flamingo/v3/framework/flamingo" "github.com/go-test/deep" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestInMemoryBehaviour_CleanCart(t *testing.T) { @@ -37,15 +38,6 @@ func TestInMemoryBehaviour_CleanCart(t *testing.T) { newInMemoryStorage(), nil, flamingo.NullLogger{}, - func() *domaincart.ItemBuilder { - return &domaincart.ItemBuilder{} - }, - func() *domaincart.DeliveryBuilder { - return &domaincart.DeliveryBuilder{} - }, - func() *domaincart.Builder { - return &domaincart.Builder{} - }, nil, nil, nil, @@ -164,15 +156,6 @@ func TestInMemoryBehaviour_CleanDelivery(t *testing.T) { newInMemoryStorage(), nil, flamingo.NullLogger{}, - func() *domaincart.ItemBuilder { - return &domaincart.ItemBuilder{} - }, - func() *domaincart.DeliveryBuilder { - return &domaincart.DeliveryBuilder{} - }, - func() *domaincart.Builder { - return &domaincart.Builder{} - }, nil, nil, nil, @@ -205,7 +188,7 @@ func TestInMemoryBehaviour_ApplyVoucher(t *testing.T) { tests := []struct { name string args args - want *domaincart.Cart + want []domaincart.CouponCode wantErr bool }{ { @@ -214,11 +197,9 @@ func TestInMemoryBehaviour_ApplyVoucher(t *testing.T) { cart: &domaincart.Cart{}, voucherCode: "valid_voucher", }, - want: &domaincart.Cart{ - AppliedCouponCodes: []domaincart.CouponCode{ - { - Code: "valid_voucher", - }, + want: []domaincart.CouponCode{ + { + Code: "valid_voucher", }, }, wantErr: false, @@ -240,9 +221,6 @@ func TestInMemoryBehaviour_ApplyVoucher(t *testing.T) { newInMemoryStorage(), nil, flamingo.NullLogger{}, - nil, - nil, - nil, &DefaultVoucherHandler{}, &DefaultGiftCardHandler{}, nil, @@ -252,8 +230,8 @@ func TestInMemoryBehaviour_ApplyVoucher(t *testing.T) { t.Errorf("DefaultCartBehaviour.ApplyVoucher() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("DefaultCartBehaviour.ApplyVoucher() got = %v, want %v", got, tt.want) + if !tt.wantErr { + assert.Equal(t, tt.want, got.AppliedCouponCodes) } }) } @@ -269,7 +247,7 @@ func TestInMemoryBehaviour_RemoveVoucher(t *testing.T) { tests := []struct { name string args args - want *domaincart.Cart + want []domaincart.CouponCode }{ { name: "Remove voucher from cart with vouchers", @@ -284,11 +262,9 @@ func TestInMemoryBehaviour_RemoveVoucher(t *testing.T) { }, couponCodeToRemove: "dummy-voucher-20", }, - want: &domaincart.Cart{ - AppliedCouponCodes: []domaincart.CouponCode{ - {Code: "OFF20"}, - {Code: "SALE"}, - }, + want: []domaincart.CouponCode{ + {Code: "OFF20"}, + {Code: "SALE"}, }, }, { @@ -298,7 +274,7 @@ func TestInMemoryBehaviour_RemoveVoucher(t *testing.T) { cart: &domaincart.Cart{}, couponCodeToRemove: "dummy-voucher-20", }, - want: &domaincart.Cart{}, + want: nil, }, { name: "Remove voucher from cart that does not exist", @@ -313,12 +289,10 @@ func TestInMemoryBehaviour_RemoveVoucher(t *testing.T) { }, couponCodeToRemove: "non-existing-voucher", }, - want: &domaincart.Cart{ - AppliedCouponCodes: []domaincart.CouponCode{ - {Code: "OFF20"}, - {Code: "dummy-voucher-20"}, - {Code: "SALE"}, - }, + want: []domaincart.CouponCode{ + {Code: "OFF20"}, + {Code: "dummy-voucher-20"}, + {Code: "SALE"}, }, }, } @@ -329,15 +303,6 @@ func TestInMemoryBehaviour_RemoveVoucher(t *testing.T) { newInMemoryStorage(), nil, flamingo.NullLogger{}, - func() *domaincart.ItemBuilder { - return &domaincart.ItemBuilder{} - }, - func() *domaincart.DeliveryBuilder { - return &domaincart.DeliveryBuilder{} - }, - func() *domaincart.Builder { - return &domaincart.Builder{} - }, &DefaultVoucherHandler{}, &DefaultGiftCardHandler{}, nil, @@ -347,11 +312,9 @@ func TestInMemoryBehaviour_RemoveVoucher(t *testing.T) { t.Fatalf("cart could not be initialized") } - got, _, _ := cob.RemoveVoucher(tt.args.ctx, tt.args.cart, tt.args.couponCodeToRemove) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("DefaultCartBehaviour.RemoveVoucher() got = %v, want %v", got, tt.want) - } - + got, _, err := cob.RemoveVoucher(tt.args.ctx, tt.args.cart, tt.args.couponCodeToRemove) + require.NoError(t, err) + assert.Equal(t, tt.want, got.AppliedCouponCodes) }) } } @@ -365,22 +328,20 @@ func TestInMemoryBehaviour_ApplyGiftCard(t *testing.T) { tests := []struct { name string args args - want *domaincart.Cart + want []domaincart.AppliedGiftCard wantErr bool }{ { name: "apply valid giftcard - success", args: args{ - cart: &domaincart.Cart{}, + cart: &domaincart.Cart{DefaultCurrency: "$"}, giftCardCode: "valid_giftcard", }, - want: &domaincart.Cart{ - AppliedGiftCards: []domaincart.AppliedGiftCard{ - { - Code: "valid_giftcard", - Applied: priceDomain.NewFromInt(10, 100, "$"), - Remaining: priceDomain.NewFromInt(0, 100, "$"), - }, + want: []domaincart.AppliedGiftCard{ + { + Code: "valid_giftcard", + Applied: priceDomain.NewFromInt(10, 100, "$"), + Remaining: priceDomain.NewFromInt(0, 100, "$"), }, }, wantErr: false, @@ -402,20 +363,21 @@ func TestInMemoryBehaviour_ApplyGiftCard(t *testing.T) { newInMemoryStorage(), nil, flamingo.NullLogger{}, - nil, - nil, - nil, &DefaultVoucherHandler{}, &DefaultGiftCardHandler{}, - nil, + &struct { + DefaultTaxRate float64 `inject:"config:commerce.cart.defaultCartAdapter.defaultTaxRate,optional"` + ProductPricing string `inject:"config:commerce.cart.defaultCartAdapter.productPrices"` + DefaultCurrency string `inject:"config:commerce.cart.defaultCartAdapter.defaultCurrency"` + }{DefaultCurrency: "$"}, ) got, _, err := cob.ApplyGiftCard(context.Background(), tt.args.cart, tt.args.giftCardCode) if (err != nil) != tt.wantErr { t.Errorf("DefaultCartBehaviour.ApplyGiftCard() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("DefaultCartBehaviour.ApplyGiftCard() got = %v, want %v", got, tt.want) + if !tt.wantErr { + assert.Equal(t, tt.want, got.AppliedGiftCards) } }) } @@ -430,7 +392,7 @@ func TestInMemoryBehaviour_RemoveGiftCard(t *testing.T) { tests := []struct { name string args args - want *domaincart.Cart + want []domaincart.AppliedGiftCard wantErr bool }{ { @@ -452,13 +414,11 @@ func TestInMemoryBehaviour_RemoveGiftCard(t *testing.T) { }, giftCardCode: "to-remove", }, - want: &domaincart.Cart{ - AppliedGiftCards: []domaincart.AppliedGiftCard{ - { - Code: "valid", - Applied: priceDomain.NewFromInt(10, 100, "$"), - Remaining: priceDomain.NewFromInt(0, 100, "$"), - }, + want: []domaincart.AppliedGiftCard{ + { + Code: "valid", + Applied: priceDomain.NewFromInt(10, 100, "$"), + Remaining: priceDomain.NewFromInt(0, 100, "$"), }, }, wantErr: false, @@ -471,9 +431,6 @@ func TestInMemoryBehaviour_RemoveGiftCard(t *testing.T) { newInMemoryStorage(), nil, flamingo.NullLogger{}, - nil, - nil, - nil, &DefaultVoucherHandler{}, &DefaultGiftCardHandler{}, nil, @@ -483,8 +440,8 @@ func TestInMemoryBehaviour_RemoveGiftCard(t *testing.T) { t.Errorf("DefaultCartBehaviour.ApplyGiftCard() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("DefaultCartBehaviour.ApplyGiftCard() got = %v, want %v", got, tt.want) + if !tt.wantErr { + assert.Equal(t, tt.want, got.AppliedGiftCards) } }) } @@ -501,16 +458,13 @@ func TestInMemoryBehaviour_Complete(t *testing.T) { nil, nil, nil, - nil, - nil, - nil, ) cart, err := cob.StoreNewCart(context.Background(), &domaincart.Cart{ID: "test-id"}) assert.NoError(t, err) got, _, err := cob.Complete(context.Background(), cart) assert.NoError(t, err) - assert.Equal(t, got, cart) + assert.Equal(t, cart, got) _, err = cob.GetCart(context.Background(), "test-id") assert.Error(t, err, "Cart should not be stored any more") @@ -528,9 +482,6 @@ func TestInMemoryBehaviour_Restore(t *testing.T) { nil, nil, nil, - nil, - nil, - nil, ) cart := &domaincart.Cart{ID: "1234"} @@ -548,3 +499,56 @@ func newInMemoryStorage() *InMemoryCartStorage { return result } + +func TestDefaultCartBehaviour_createCartItemFromProduct(t *testing.T) { + t.Run("gross", func(t *testing.T) { + cob := DefaultCartBehaviour{} + cob.Inject(nil, nil, flamingo.NullLogger{}, nil, nil, &struct { + DefaultTaxRate float64 `inject:"config:commerce.cart.defaultCartAdapter.defaultTaxRate,optional"` + ProductPricing string `inject:"config:commerce.cart.defaultCartAdapter.productPrices"` + DefaultCurrency string `inject:"config:commerce.cart.defaultCartAdapter.defaultCurrency"` + }{ProductPricing: "gross", DefaultTaxRate: 10.0, DefaultCurrency: "€"}) + + item, err := cob.createCartItemFromProduct(2, "ma", "", map[string]string{}, domain.SimpleProduct{ + Saleable: domain.Saleable{ + IsSaleable: true, + ActivePrice: domain.PriceInfo{ + Default: priceDomain.NewFromFloat(50.00, "USD"), + }, + }, + }) + + require.NoError(t, err) + assert.True(t, item.SinglePriceGross.Equal(priceDomain.NewFromFloat(50.00, "USD"))) + assert.Equal(t, 45.45, item.SinglePriceNet.FloatAmount()) + assert.Equal(t, 100.00, item.RowPriceGross.FloatAmount()) + assert.Equal(t, 45.45*2, item.RowPriceNet.FloatAmount()) + assert.Equal(t, 100.00-45.45*2, item.TotalTaxAmount().FloatAmount()) + }) + + t.Run("net", func(t *testing.T) { + cob := DefaultCartBehaviour{} + cob.Inject(nil, nil, flamingo.NullLogger{}, nil, nil, &struct { + DefaultTaxRate float64 `inject:"config:commerce.cart.defaultCartAdapter.defaultTaxRate,optional"` + ProductPricing string `inject:"config:commerce.cart.defaultCartAdapter.productPrices"` + DefaultCurrency string `inject:"config:commerce.cart.defaultCartAdapter.defaultCurrency"` + }{ProductPricing: "net", DefaultTaxRate: 10.0, DefaultCurrency: "€"}) + + item, err := cob.createCartItemFromProduct(2, "ma", "", map[string]string{}, domain.SimpleProduct{ + Saleable: domain.Saleable{ + IsSaleable: true, + ActivePrice: domain.PriceInfo{ + Default: priceDomain.NewFromFloat(50.00, "USD"), + }, + }, + }) + + require.NoError(t, err) + assert.True(t, item.SinglePriceNet.Equal(priceDomain.NewFromFloat(50.00, "USD"))) + assert.Equal(t, 55.00, item.SinglePriceGross.FloatAmount()) + assert.Equal(t, 55.00*2, item.RowPriceGross.FloatAmount()) + assert.Equal(t, 50.00*2, item.RowPriceNet.FloatAmount()) + assert.Equal(t, 10.0, item.TotalTaxAmount().FloatAmount()) + }) + +} diff --git a/cart/infrastructure/defaultGuestCartserviceAdapter.go b/cart/infrastructure/defaultGuestCartserviceAdapter.go index 3c59b173f..2b4b4610e 100644 --- a/cart/infrastructure/defaultGuestCartserviceAdapter.go +++ b/cart/infrastructure/defaultGuestCartserviceAdapter.go @@ -2,10 +2,11 @@ package infrastructure import ( "context" - "flamingo.me/flamingo-commerce/v3/cart/domain/cart" - "flamingo.me/flamingo/v3/framework/flamingo" "math/rand" "strconv" + + "flamingo.me/flamingo-commerce/v3/cart/domain/cart" + "flamingo.me/flamingo/v3/framework/flamingo" ) type ( @@ -49,6 +50,6 @@ func (gcs *DefaultGuestCartService) GetModifyBehaviour(context.Context) (cart.Mo // Deprecated: (deprecated in the interface) func (gcs *DefaultGuestCartService) RestoreCart(ctx context.Context, cart cart.Cart) (*cart.Cart, error) { // RestoreCart restores a previously used cart - gcs.logger.Warn("RestoreCart depricated") + gcs.logger.Warn("RestoreCart deprecated") return &cart, nil } diff --git a/cart/infrastructure/placeorder/logger.go b/cart/infrastructure/placeorder/logger.go index eab26994a..c6df6526c 100644 --- a/cart/infrastructure/placeorder/logger.go +++ b/cart/infrastructure/placeorder/logger.go @@ -4,16 +4,17 @@ import ( "context" "encoding/json" "errors" - cartDomain "flamingo.me/flamingo-commerce/v3/cart/domain/cart" - "flamingo.me/flamingo-commerce/v3/cart/domain/placeorder" - "flamingo.me/flamingo/v3/core/auth" - "flamingo.me/flamingo/v3/framework/flamingo" "fmt" - "golang.org/x/mod/modfile" "io/ioutil" "os" "path" "time" + + cartDomain "flamingo.me/flamingo-commerce/v3/cart/domain/cart" + "flamingo.me/flamingo-commerce/v3/cart/domain/placeorder" + "flamingo.me/flamingo/v3/core/auth" + "flamingo.me/flamingo/v3/framework/flamingo" + "golang.org/x/mod/modfile" ) type ( @@ -78,15 +79,15 @@ func (e *PlaceOrderLoggerAdapter) placeCart(cart *cartDomain.Cart, payment *plac // checkPayment func (e *PlaceOrderLoggerAdapter) checkPayment(cart *cartDomain.Cart, payment *placeorder.Payment) error { - if payment == nil && cart.GrandTotal().IsPositive() { + if payment == nil && cart.GrandTotal.IsPositive() { return errors.New("No valid Payment given") } - if cart.GrandTotal().IsPositive() { + if cart.GrandTotal.IsPositive() { totalPrice, err := payment.TotalValue() if err != nil { return err } - if !totalPrice.Equal(cart.GrandTotal()) { + if !totalPrice.Equal(cart.GrandTotal) { return errors.New("Payment Total does not match with Grandtotal") } } diff --git a/cart/infrastructure/placeorder/logger_test.go b/cart/infrastructure/placeorder/logger_test.go index 2434c614d..d1b7ad4ed 100644 --- a/cart/infrastructure/placeorder/logger_test.go +++ b/cart/infrastructure/placeorder/logger_test.go @@ -2,13 +2,14 @@ package logger_test import ( "context" + "testing" + "flamingo.me/flamingo-commerce/v3/cart/domain/cart" "flamingo.me/flamingo-commerce/v3/cart/domain/placeorder" logger "flamingo.me/flamingo-commerce/v3/cart/infrastructure/placeorder" "flamingo.me/flamingo-commerce/v3/price/domain" "flamingo.me/flamingo/v3/framework/flamingo" "github.com/stretchr/testify/assert" - "testing" ) type ( @@ -210,8 +211,8 @@ func TestPlaceOrderLoggerAdapter_PlaceGuestCart(t *testing.T) { placeorder.Transaction{ Method: "testmethod", Status: placeorder.PaymentStatusOpen, - ValuedAmountPayed: exampleCart.GrandTotal(), - AmountPayed: exampleCart.GrandTotal(), + ValuedAmountPayed: exampleCart.GrandTotal, + AmountPayed: exampleCart.GrandTotal, TransactionID: "t1", }, }, diff --git a/cart/interfaces/graphql/dto/cartSummary.go b/cart/interfaces/graphql/dto/cartSummary.go index 9da4c69fa..b62c83a25 100644 --- a/cart/interfaces/graphql/dto/cartSummary.go +++ b/cart/interfaces/graphql/dto/cartSummary.go @@ -6,7 +6,7 @@ import ( ) type ( - // CartSummary – provides custom graphql interface methods + // CartSummary provides custom graphql interface methods CartSummary struct { cart *cart.Cart } @@ -30,49 +30,33 @@ func (cs *CartSummary) HasAppliedDiscounts() bool { return result } -// SumTotalDiscountWithGiftCardsAmount – returns sum price of total discounts with applied gift cards +// SumTotalDiscountWithGiftCardsAmount returns sum price of total discounts with applied gift cards func (cs *CartSummary) SumTotalDiscountWithGiftCardsAmount() domain.Price { - totalDiscount := cs.cart.SumTotalDiscountAmount() - appliedGiftCardsAmount, _ := cs.cart.SumAppliedGiftCards() + totalDiscount := cs.cart.TotalDiscountAmount + appliedGiftCardsAmount := cs.cart.TotalGiftCardAmount price, _ := totalDiscount.Sub(appliedGiftCardsAmount) return price } -// SumAppliedDiscounts – returns the sum of the applied values of the AppliedDiscounts -func (cs CartSummary) SumAppliedDiscounts() *domain.Price { - result, err := cs.cart.MergeDiscounts() - if err != nil { - return nil - } - - sum, err := result.Sum() - if err != nil { - return nil - } +// TotalDiscountAmount returns the sum of the applied values of the AppliedDiscounts +func (cs CartSummary) TotalDiscountAmount() *domain.Price { + sum := cs.cart.TotalDiscountAmount return &sum } -// SumAppliedGiftCards – sums applied gift cards -func (cs CartSummary) SumAppliedGiftCards() *domain.Price { - sum, err := cs.cart.SumAppliedGiftCards() - if err != nil { - return nil - } - return &sum +// TotalGiftCardAmount sums applied gift cards +func (cs CartSummary) TotalGiftCardAmount() *domain.Price { + return &cs.cart.TotalGiftCardAmount } -// SumGrandTotalWithGiftCards – sums grand total with gift cards -func (cs CartSummary) SumGrandTotalWithGiftCards() *domain.Price { - sum, err := cs.cart.SumGrandTotalWithGiftCards() - if err != nil { - return nil - } - return &sum +// GrandTotalWithGiftCards sums grand total with gift cards +func (cs CartSummary) GrandTotalWithGiftCards() *domain.Price { + return &cs.cart.GrandTotalWithGiftCards } -// SumTaxes – sums taxes +// SumTaxes sums taxes func (cs CartSummary) SumTaxes() *Taxes { items := cs.cart.SumTaxes() taxes := make([]cart.Tax, 0, len(items)) @@ -87,7 +71,7 @@ func (cs CartSummary) SumTaxes() *Taxes { return &Taxes{Items: taxes} } -// SumPaymentSelectionCartSplitValueAmountByMethods – sum +// SumPaymentSelectionCartSplitValueAmountByMethods sum func (cs CartSummary) SumPaymentSelectionCartSplitValueAmountByMethods(methods []string) *domain.Price { if cs.cart.PaymentSelection == nil { return nil diff --git a/cart/interfaces/graphql/schema.graphql b/cart/interfaces/graphql/schema.graphql index c33af899b..a7d9ff85a 100644 --- a/cart/interfaces/graphql/schema.graphql +++ b/cart/interfaces/graphql/schema.graphql @@ -8,9 +8,9 @@ type Commerce_Cart_DecoratedCart { type Commerce_Cart_Summary { discounts: Commerce_Cart_AppliedDiscounts! - sumAppliedDiscounts: Commerce_Price - sumAppliedGiftCards: Commerce_Price - sumGrandTotalWithGiftCards: Commerce_Price + totalDiscountAmount: Commerce_Price + totalGiftCardAmount: Commerce_Price + grandTotalWithGiftCards: Commerce_Price sumTotalDiscountWithGiftCardsAmount: Commerce_Price hasAppliedDiscounts: Boolean! sumTaxes: Commerce_Cart_Taxes @@ -52,10 +52,10 @@ type Commerce_Cart_Cart { getVoucherSavings: Commerce_Price! getCartTeaser: Commerce_Cart_Teaser! - sumShippingNet: Commerce_Price! - sumShippingNetWithDiscounts: Commerce_Price! - sumShippingGross: Commerce_Price! - sumShippingGrossWithDiscounts: Commerce_Price! + shippingNet: Commerce_Price! + shippingNetWithDiscounts: Commerce_Price! + shippingGross: Commerce_Price! + shippingGrossWithDiscounts: Commerce_Price! hasShippingCosts: Boolean! allShippingTitles: [String!] @@ -63,9 +63,9 @@ type Commerce_Cart_Cart { subTotalGross: Commerce_Price! subTotalGrossWithDiscounts: Commerce_Price! subTotalNetWithDiscounts: Commerce_Price! - sumTotalDiscountAmount: Commerce_Price! - sumNonItemRelatedDiscountAmount: Commerce_Price! - sumItemRelatedDiscountAmount: Commerce_Price! + totalDiscountAmount: Commerce_Price! + nonItemRelatedDiscountAmount: Commerce_Price! + itemRelatedDiscountAmount: Commerce_Price! hasAppliedCouponCode: Boolean! getPaymentReference: String! @@ -150,9 +150,9 @@ type Commerce_Cart_Delivery { grandTotal: Commerce_Price sumTotalTaxAmount: Commerce_Price subTotalNet: Commerce_Price - sumTotalDiscountAmount: Commerce_Price - sumNonItemRelatedDiscountAmount: Commerce_Price - sumItemRelatedDiscountAmount: Commerce_Price + totalDiscountAmount: Commerce_Price + nonItemRelatedDiscountAmount: Commerce_Price + itemRelatedDiscountAmount: Commerce_Price subTotalGrossWithDiscounts: Commerce_Price subTotalNetWithDiscounts: Commerce_Price! hasItems: Boolean! diff --git a/cart/migration.sed b/cart/migration.sed new file mode 100644 index 000000000..3389dd893 --- /dev/null +++ b/cart/migration.sed @@ -0,0 +1,35 @@ +# cart item +s/\.RowPriceGrossWithDiscount()/.RowPriceGrossWithDiscount/ +s/\.RowPriceGrossWithItemRelatedDiscount()/.RowPriceGrossWithItemRelatedDiscount/ +s/\.RowPriceNetWithDiscount()/.RowPriceNetWithDiscount/ +s/\.RowPriceNetWithItemRelatedDiscount()/.RowPriceNetWithItemRelatedDiscount/ +s/\.TotalDiscountAmount()/.TotalDiscountAmount/ +s/\.ItemRelatedDiscountAmount()/.ItemRelatedDiscountAmount/ +s/\.NonItemRelatedDiscountAmount()/.NonItemRelatedDiscountAmount/ +# shipping item +s/\.TotalWithDiscountInclTax()/.PriceGrossWithDiscounts/ +# delivery +s/\.SubTotalGross()/.SubTotalGross/ +s/\.SubTotalNet()/.SubTotalNet/ +s/\.SumTotalDiscountAmount()/.TotalDiscountAmount/ +s/\.SumSubTotalDiscountAmount()/.SubTotalDiscountAmount/ +s/\.SumNonItemRelatedDiscountAmount()/.NonItemRelatedDiscountAmount/ +s/\.SumItemRelatedDiscountAmount()/.ItemRelatedDiscountAmount/ +s/\.SubTotalGrossWithDiscounts()/.SubTotalGrossWithDiscounts/ +s/\.SubTotalNetWithDiscounts()/.SubTotalNetWithDiscounts/ +s/\.GrandTotal()/.GrandTotal/ +# cart +s/\.GrandTotal()/.GrandTotal/ +s/\.SumShippingNet()/.ShippingNet/ +s/\.SumShippingNetWithDiscounts()/.ShippingNetWithDiscounts/ +s/\.SumShippingGross()/.ShippingGross/ +s/\.SumShippingGrossWithDiscounts()/.ShippingGrossWithDiscounts/ +s/\.SubTotalGross()/.SubTotalGross/ +s/\.SubTotalNet()/.SubTotalNet/ +s/\.SubTotalGrossWithDiscounts()/.SubTotalGrossWithDiscounts/ +s/\.SubTotalNetWithDiscounts()/.SubTotalNetWithDiscounts/ +s/\.SumTotalDiscountAmount()/.TotalDiscountAmount/ +s/\.SumNonItemRelatedDiscountAmount()/.NonItemRelatedDiscountAmount/ +s/\.SumItemRelatedDiscountAmount()/.ItemRelatedDiscountAmount/ +s/\.SumAppliedGiftCards()/.TotalGiftCardAmount/ +s/\.SumGrandTotalWithGiftCards()/.GrandTotalWithGiftCards/ diff --git a/cart/module.go b/cart/module.go index d9b2b6492..77ee093f3 100644 --- a/cart/module.go +++ b/cart/module.go @@ -102,6 +102,8 @@ commerce: { enabled: bool | *true storage: "inmemory" defaultTaxRate?: number + productPrices: *"gross" | "net" + defaultCurrency: string | *"€" } placeOrderLogger: { enabled: bool | *true diff --git a/checkout/Changelog.md b/checkout/Changelog.md deleted file mode 100644 index 3a489576c..000000000 --- a/checkout/Changelog.md +++ /dev/null @@ -1,28 +0,0 @@ -# 2. June 2020 -* Deprecate sourcing in checkout (commerce.checkout.activateDeprecatedSourcing) - -# 20. August 2019 -* Add new PaymentAction which processes the payment flow status -* Add option to place an order as early as the payment is started - -# 18. December 2019 -* Reduce calls for updating items in `SetSourcesForCartItems` - -# 7. January 2020 -* Generate a new Idempotency Key in the PaymentSelection if an payment error occurs (canceled / aborted by customer) to allow the customer to retry - -# 12. February 2020 -* Move config to commerce namespace, from `checkout` to `commerce.checkout` -* Add cue based config - -# 20. February 2020 -* Add `OrderService.CancelOrderWithoutRestore()` which uses the new `CartService` function -* Add `OrderService.CartPlaceOrder()` to place a provided cart instead of fetching it from the `CartService` - -* Add new GraphQL Place Order process which relies on a new state machine please referer to the module readme for more details - * Transition all actions of the checkout controller to separate states - * Add new `ContextStore` port to provide a storage for the place order process - * Provide InMemory and Redis Adapter - * Add new `TryLocker` port to provide an easy way to sync multiple order processes across different nodes - * Provide InMemory and Redis Adapter - * Breaking: Add new GraphQL mutations / queries to start / stop / refresh the place order process diff --git a/checkout/domain/placeorder/states/place_order.go b/checkout/domain/placeorder/states/place_order.go index 959faa7ed..a3f8b26ad 100644 --- a/checkout/domain/placeorder/states/place_order.go +++ b/checkout/domain/placeorder/states/place_order.go @@ -65,7 +65,7 @@ func (po PlaceOrder) Run(ctx context.Context, p *process.Process) process.RunRes decoratedCart := po.cartDecoratorFactory.Create(ctx, cart) payment := &placeorder.Payment{} - if !cart.GrandTotal().IsZero() { + if !cart.GrandTotal.IsZero() { paymentGateway, err := po.paymentService.PaymentGatewayByCart(cart) if err != nil { return process.RunResult{ diff --git a/checkout/domain/placeorder/states/prepare_cart.go b/checkout/domain/placeorder/states/prepare_cart.go index fbf207d4c..705b8b7f5 100644 --- a/checkout/domain/placeorder/states/prepare_cart.go +++ b/checkout/domain/placeorder/states/prepare_cart.go @@ -46,7 +46,7 @@ func (v PrepareCart) Run(ctx context.Context, p *process.Process) process.RunRes } } - if c.GrandTotal().IsZero() { + if c.GrandTotal.IsZero() { p.UpdateState(ValidateCart{}.Name(), nil) p.UpdateCart(*c) return process.RunResult{} diff --git a/checkout/domain/placeorder/states/validate_cart.go b/checkout/domain/placeorder/states/validate_cart.go index f29c29df5..13c53a844 100644 --- a/checkout/domain/placeorder/states/validate_cart.go +++ b/checkout/domain/placeorder/states/validate_cart.go @@ -53,7 +53,7 @@ func (v ValidateCart) Run(ctx context.Context, p *process.Process) process.RunRe } } - if p.Context().Cart.GrandTotal().IsZero() { + if p.Context().Cart.GrandTotal.IsZero() { p.UpdateState(CompleteCart{}.Name(), nil) return process.RunResult{} } diff --git a/checkout/domain/placeorder/states/validate_cart_test.go b/checkout/domain/placeorder/states/validate_cart_test.go index 1fae571cf..74942ddd9 100644 --- a/checkout/domain/placeorder/states/validate_cart_test.go +++ b/checkout/domain/placeorder/states/validate_cart_test.go @@ -124,32 +124,15 @@ func TestValidateCart_Run(t *testing.T) { state := new(states.ValidateCart).Inject(&cartService) p := &process.Process{} cart := cartDomain.Cart{ - ID: "cart-id", - EntityID: "entity-id", - Deliveries: []cartDomain.Delivery{ - { - Cartitems: []cartDomain.Item{ - { - ID: "1", - Qty: 1, - SinglePriceGross: domain.NewFromInt(1, 1, "EUR"), - RowPriceGross: domain.NewFromInt(1, 1, "EUR"), - RowPriceNet: domain.NewFromInt(1, 1, "EUR"), - SinglePriceNet: domain.NewFromInt(1, 1, "EUR"), - }, - }, - }, - }, + ID: "cart-id", + EntityID: "entity-id", + GrandTotal: domain.NewFromInt(1, 1, "EUR"), } if tt.isGrandTotalZero { - cart.Deliveries[0].Cartitems[0].AppliedDiscounts = []cartDomain.AppliedDiscount{ - { - CampaignCode: "test", - Applied: domain.NewFromInt(-1, 1, "EUR"), - }, - } + cart.GrandTotal = domain.NewFromInt(0, 1, "EUR") } + p.UpdateCart(cart) p.UpdateState(state.Name(), nil) ctx := web.ContextWithSession(context.Background(), web.EmptySession()) diff --git a/checkout/interfaces/controller/checkoutcontroller.go b/checkout/interfaces/controller/checkoutcontroller.go index 431315133..df51e207f 100644 --- a/checkout/interfaces/controller/checkoutcontroller.go +++ b/checkout/interfaces/controller/checkoutcontroller.go @@ -269,7 +269,7 @@ func (cc *CheckoutController) placeOrderAction(ctx context.Context, r *web.Reque placedOrderInfo, _ = cc.orderService.LastPlacedOrder(ctx) cc.orderService.ClearLastPlacedOrder(ctx) } else { - if decoratedCart.Cart.GrandTotal().IsZero() { + if decoratedCart.Cart.GrandTotal.IsZero() { // Nothing to pay, so cart can be placed without payment processing. placedOrderInfo, err = cc.orderService.CurrentCartPlaceOrder(ctx, session, placeorder.Payment{}) } else { @@ -474,7 +474,7 @@ func (cc *CheckoutController) processPayment(ctx context.Context, r *web.Request } // Cart grand total is zero, so no payment needed. - if decoratedCart.Cart.GrandTotal().IsZero() { + if decoratedCart.Cart.GrandTotal.IsZero() { return cc.responder.RouteRedirect("checkout.placeorder", nil) } @@ -561,7 +561,7 @@ func (cc *CheckoutController) ReviewAction(ctx context.Context, r *web.Request) return cc.processPayment(ctx, r) } - if decoratedCart.Cart.GrandTotal().IsZero() { + if decoratedCart.Cart.GrandTotal.IsZero() { return cc.responder.RouteRedirect("checkout.placeorder", nil) } } diff --git a/checkout/interfaces/controller/forms/checkoutform.go b/checkout/interfaces/controller/forms/checkoutform.go index 89a6b90bc..7341adb75 100644 --- a/checkout/interfaces/controller/forms/checkoutform.go +++ b/checkout/interfaces/controller/forms/checkoutform.go @@ -220,7 +220,7 @@ func (c *CheckoutFormController) HandleFormAction(ctx context.Context, r *web.Re } } - if !cart.GrandTotal().IsZero() { + if !cart.GrandTotal.IsZero() { // 4. ### Add the simplePaymentForm if payment is required. simplePaymentForm, success, err := c.simplePaymentFormController.HandleFormAction(ctx, newRequestWithResolvedNamespace("payment", r)) overallSuccess = overallSuccess && success diff --git a/docs/openapi/docs.go b/docs/openapi/docs.go index 7ba5c02d4..fcd3253a8 100644 --- a/docs/openapi/docs.go +++ b/docs/openapi/docs.go @@ -1030,7 +1030,7 @@ var doc = `{ "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/cartResultError" + "$ref": "#/definitions/paymentResultError" } } } @@ -1113,6 +1113,26 @@ var doc = `{ "$ref": "#/definitions/CategoryAttribute" } }, + "ProductMedia": { + "type": "object", + "properties": { + "MimeType": { + "type": "string" + }, + "Reference": { + "type": "string" + }, + "Title": { + "type": "string" + }, + "Type": { + "type": "string" + }, + "Usage": { + "type": "string" + } + } + }, "application.PlaceOrderPaymentInfo": { "type": "object", "properties": { @@ -1315,17 +1335,74 @@ var doc = `{ "description": "EntityID is a second identifier that may be used by some backends", "type": "string" }, + "GrandTotal": { + "description": "GrandTotal is the final amount that need to be paid by the customer (gross)", + "$ref": "#/definitions/domain.Price" + }, + "GrandTotalWithGiftCards": { + "description": "GrandTotalWithGiftCards is the final amount with the applied gift cards subtracted.", + "$ref": "#/definitions/domain.Price" + }, "ID": { "description": "ID is the main identifier of the cart", "type": "string" }, + "ItemRelatedDiscountAmount": { + "description": "ItemRelatedDiscountAmount is the sum of discounts that are related to the item (including shipping discounts)", + "$ref": "#/definitions/domain.Price" + }, + "NonItemRelatedDiscountAmount": { + "description": "NonItemRelatedDiscountAmount is the sum of discounts that are not related to the item (including shipping discounts)", + "$ref": "#/definitions/domain.Price" + }, "PaymentSelection": { - "description": "PaymentSelection is used to store information on \"how\" the customer wants to pay" + "description": "PaymentSelection is used to store information on \"how\" the customer wants to pay", + "$ref": "#/definitions/cart.PaymentSelection" }, "Purchaser": { "description": "Purchaser hold additional infos for the legal contact person in this order", "$ref": "#/definitions/cart.Person" }, + "ShippingGross": { + "description": "ShippingGross is the sum of all shipping costs including tax", + "$ref": "#/definitions/domain.Price" + }, + "ShippingGrossWithDiscounts": { + "description": "ShippingGrossWithDiscounts is the sum of all shipping costs with all shipping discounts including tax", + "$ref": "#/definitions/domain.Price" + }, + "ShippingNet": { + "description": "ShippingNet is the sum of all shipping costs", + "$ref": "#/definitions/domain.Price" + }, + "ShippingNetWithDiscounts": { + "description": "ShippingNetWithDiscounts is the sum of all shipping costs with all shipping discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalGross": { + "description": "SubTotalGross is the sum of all delivery subtotals (without shipping/ discounts)", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalGrossWithDiscounts": { + "description": "SubTotalGrossWithDiscounts is the sum of row gross prices reduced by the applied discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalNet": { + "description": "SubTotalNet is the sum of all delivery net subtotals (without shipping/ discounts)", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalNetWithDiscounts": { + "description": "SubTotalNetWithDiscounts is the sum of row net prices reduced by the net value of the applied discounts", + "$ref": "#/definitions/domain.Price" + }, + "TotalDiscountAmount": { + "description": "TotalDiscountAmount is the sum of all discounts (incl. shipping)", + "$ref": "#/definitions/domain.Price" + }, + "TotalGiftCardAmount": { + "description": "AppliedGiftCardsAmount is the part of GrandTotal which is paid by gift cards", + "$ref": "#/definitions/domain.Price" + }, "Totalitems": { "description": "Additional non taxable totals", "type": "array", @@ -1362,9 +1439,45 @@ var doc = `{ "description": "DeliveryInfo contains details for this delivery e.g. how and where the delivery should be delivered to", "$ref": "#/definitions/cart.DeliveryInfo" }, + "GrandTotal": { + "description": "GrandTotal contains the final price to pay", + "$ref": "#/definitions/domain.Price" + }, + "ItemRelatedDiscountAmount": { + "description": "ItemRelatedDiscountAmount contains the sum of discounts that are related to the item, e.g. promo due to product attribute", + "$ref": "#/definitions/domain.Price" + }, + "NonItemRelatedDiscountAmount": { + "description": "NonItemRelatedDiscountAmount contains the sum of discounts that are not related to the item, e.g. a general promo", + "$ref": "#/definitions/domain.Price" + }, "ShippingItem": { "description": "ShippingItem\trepresent the shipping cost that may be involved in this delivery", "$ref": "#/definitions/cart.ShippingItem" + }, + "SubTotalDiscountAmount": { + "description": "TotalDiscountAmount contains the sum of all discounts (excl. shipping)", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalGross": { + "description": "SubTotalGross contains the sum of row gross prices, without shipping/discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalGrossWithDiscounts": { + "description": "SubTotalGrossWithDiscounts contains the sum of row gross prices reduced by the applied discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalNet": { + "description": "SubTotalNet contains the sum of row net prices, without shipping/discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalNetWithDiscounts": { + "description": "SubTotalNetWithDiscounts contains the sum of row net prices reduced by the net value of the applied discounts", + "$ref": "#/definitions/domain.Price" + }, + "TotalDiscountAmount": { + "description": "TotalDiscountAmount contains the sum of all discounts (incl. shipping)", + "$ref": "#/definitions/domain.Price" } } }, @@ -1444,7 +1557,7 @@ var doc = `{ } }, "AppliedDiscounts": { - "description": "AppliedDiscounts contains the details about the discounts applied to this item - they can be \"itemrelated\" or not", + "description": "AppliedDiscounts contains the details about the discounts applied to this item - they can be \"itemrelated\" or not\nitemrelated would be e.g. special price, buy 3 pay 2\nnon-itemrelated would be e.g. 10% on everything", "type": "array", "items": { "$ref": "#/definitions/cart.AppliedDiscount" @@ -1458,9 +1571,18 @@ var doc = `{ "description": "ID of the item - needs to be unique over the whole cart", "type": "string" }, + "ItemRelatedDiscountAmount": { + "description": "ItemRelatedDiscountAmount is the sum of all itemrelated Discounts", + "$ref": "#/definitions/domain.Price" + }, "MarketplaceCode": { + "description": "MarketplaceCode is the identifier for the product", "type": "string" }, + "NonItemRelatedDiscountAmount": { + "description": "NonItemRelatedDiscountAmount is the sum of non-itemrelated Discounts where IsItemRelated = false", + "$ref": "#/definitions/domain.Price" + }, "ProductName": { "type": "string" }, @@ -1468,38 +1590,61 @@ var doc = `{ "type": "integer" }, "RowPriceGross": { - "description": "RowPriceGross", + "description": "RowPriceGross is the price incl. taxes for the whole Qty of products", + "$ref": "#/definitions/domain.Price" + }, + "RowPriceGrossWithDiscount": { + "description": "RowPriceGrossWithDiscount is the price incl. taxes with deducted discounts for the whole Qty of products\nThis is in most cases the final price for the customer to pay", + "$ref": "#/definitions/domain.Price" + }, + "RowPriceGrossWithItemRelatedDiscount": { + "description": "RowPriceGrossWithItemRelatedDiscount is the price incl. taxes with deducted item related discounts for the whole Qty of products", "$ref": "#/definitions/domain.Price" }, "RowPriceNet": { - "description": "RowPriceNet", + "description": "RowPriceNet is the price excl. taxes for the whole Qty of products", + "$ref": "#/definitions/domain.Price" + }, + "RowPriceNetWithDiscount": { + "description": "RowPriceNetWithDiscount is the discounted net price for the whole Qty of products", + "$ref": "#/definitions/domain.Price" + }, + "RowPriceNetWithItemRelatedDiscount": { + "description": "RowPriceNetWithItemRelatedDiscount is the price excl. taxes with deducted item related discounts for the whole Qty of products", "$ref": "#/definitions/domain.Price" }, "RowTaxes": { - "description": "RowPriceGross", + "description": "RowTaxes is a list of all taxes applied for the given Qty of products", "type": "array", "items": { "$ref": "#/definitions/cart.Tax" } }, "SinglePriceGross": { - "description": "SinglePriceGross gross price (incl. taxes) for a single product", + "description": "SinglePriceGross is the gross price (incl. taxes) for a single product", "$ref": "#/definitions/domain.Price" }, "SinglePriceNet": { - "description": "SinglePriceNet net price (excl. taxes) for a single product", + "description": "SinglePriceNet is the net price (excl. taxes) for a single product", "$ref": "#/definitions/domain.Price" }, "SourceID": { "description": "Source Id of where the items should be initial picked - This is set by the SourcingLogic", "type": "string" }, + "TotalDiscountAmount": { + "description": "TotalDiscountAmount is the sum of all applied discounts (aka the savings for the customer)", + "$ref": "#/definitions/domain.Price" + }, "VariantMarketPlaceCode": { "description": "VariantMarketPlaceCode is used for Configurable products", "type": "string" } } }, + "cart.PaymentSelection": { + "type": "object" + }, "cart.Person": { "type": "object", "properties": { @@ -1544,9 +1689,15 @@ var doc = `{ "PriceGross": { "$ref": "#/definitions/domain.Price" }, + "PriceGrossWithDiscounts": { + "$ref": "#/definitions/domain.Price" + }, "PriceNet": { "$ref": "#/definitions/domain.Price" }, + "PriceNetWithDiscounts": { + "$ref": "#/definitions/domain.Price" + }, "TaxAmount": { "$ref": "#/definitions/domain.Price" }, @@ -1603,17 +1754,6 @@ var doc = `{ } } }, - "cartResultError": { - "type": "object", - "properties": { - "Code": { - "type": "string" - }, - "Message": { - "type": "string" - } - } - }, "checkoutError": { "type": "object", "properties": { @@ -1629,9 +1769,11 @@ var doc = `{ "type": "object", "properties": { "Error": { - "$ref": "#/definitions/cartResultError" + "$ref": "#/definitions/paymentResultError" + }, + "Product": { + "$ref": "#/definitions/domain.BasicProduct" }, - "Product": {}, "Success": { "type": "boolean" } @@ -1646,13 +1788,15 @@ var doc = `{ "CartValidationResult": { "$ref": "#/definitions/validation.Result" }, - "Data": {}, + "Data": { + "type": "object" + }, "DataValidationInfo": { "type": "object" }, "Error": { "description": "Contains details if success is false", - "$ref": "#/definitions/cartResultError" + "$ref": "#/definitions/paymentResultError" }, "Success": { "type": "boolean" @@ -1688,7 +1832,9 @@ var doc = `{ "State": { "type": "string" }, - "StateData": {}, + "StateData": { + "$ref": "#/definitions/process.StateData" + }, "UUID": { "type": "string" } @@ -1745,7 +1891,9 @@ var doc = `{ "Item": { "$ref": "#/definitions/cart.Item" }, - "Product": {} + "Product": { + "$ref": "#/definitions/domain.BasicProduct" + } } }, "decorator.DecoratedDelivery": { @@ -1768,7 +1916,9 @@ var doc = `{ "Label": { "type": "string" }, - "RawValue": {} + "RawValue": { + "type": "object" + } } }, "domain.Badge": { @@ -1782,6 +1932,9 @@ var doc = `{ } } }, + "domain.BasicProduct": { + "type": "object" + }, "domain.CategoryTeaser": { "type": "object", "properties": { @@ -1843,7 +1996,8 @@ var doc = `{ "$ref": "#/definitions/domain.FlowActionData" }, "Data": { - "description": "Data contains additional information related to the action / flow" + "description": "Data contains additional information related to the action / flow", + "type": "object" }, "Error": { "description": "Error contains additional information in case of an error (e.g. payment failed)", @@ -2064,7 +2218,9 @@ var doc = `{ }, "Media": { "type": "array", - "items": {} + "items": { + "$ref": "#/definitions/ProductMedia" + } }, "RetailerCode": { "type": "string" @@ -2121,7 +2277,9 @@ var doc = `{ "Media": { "description": "Media", "type": "array", - "items": {} + "items": { + "$ref": "#/definitions/ProductMedia" + } }, "PreSelectedVariantSku": { "description": "PreSelectedVariantSku might be set for configurables to give a hint to link to a variant of a configurable (That might be the case if a user filters for an attribute and in the teaser the variant with that attribute is shown)", @@ -2171,6 +2329,17 @@ var doc = `{ } } }, + "paymentResultError": { + "type": "object", + "properties": { + "Code": { + "type": "string" + }, + "Message": { + "type": "string" + } + } + }, "placeorder.CreditCardInfo": { "type": "object", "properties": { @@ -2199,6 +2368,9 @@ var doc = `{ } } }, + "process.StateData": { + "type": "object" + }, "validation.ItemValidationError": { "type": "object", "properties": { diff --git a/docs/openapi/swagger.json b/docs/openapi/swagger.json index 4a7b79780..0daa4baf5 100644 --- a/docs/openapi/swagger.json +++ b/docs/openapi/swagger.json @@ -1014,7 +1014,7 @@ "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/cartResultError" + "$ref": "#/definitions/paymentResultError" } } } @@ -1097,6 +1097,26 @@ "$ref": "#/definitions/CategoryAttribute" } }, + "ProductMedia": { + "type": "object", + "properties": { + "MimeType": { + "type": "string" + }, + "Reference": { + "type": "string" + }, + "Title": { + "type": "string" + }, + "Type": { + "type": "string" + }, + "Usage": { + "type": "string" + } + } + }, "application.PlaceOrderPaymentInfo": { "type": "object", "properties": { @@ -1299,17 +1319,74 @@ "description": "EntityID is a second identifier that may be used by some backends", "type": "string" }, + "GrandTotal": { + "description": "GrandTotal is the final amount that need to be paid by the customer (gross)", + "$ref": "#/definitions/domain.Price" + }, + "GrandTotalWithGiftCards": { + "description": "GrandTotalWithGiftCards is the final amount with the applied gift cards subtracted.", + "$ref": "#/definitions/domain.Price" + }, "ID": { "description": "ID is the main identifier of the cart", "type": "string" }, + "ItemRelatedDiscountAmount": { + "description": "ItemRelatedDiscountAmount is the sum of discounts that are related to the item (including shipping discounts)", + "$ref": "#/definitions/domain.Price" + }, + "NonItemRelatedDiscountAmount": { + "description": "NonItemRelatedDiscountAmount is the sum of discounts that are not related to the item (including shipping discounts)", + "$ref": "#/definitions/domain.Price" + }, "PaymentSelection": { - "description": "PaymentSelection is used to store information on \"how\" the customer wants to pay" + "description": "PaymentSelection is used to store information on \"how\" the customer wants to pay", + "$ref": "#/definitions/cart.PaymentSelection" }, "Purchaser": { "description": "Purchaser hold additional infos for the legal contact person in this order", "$ref": "#/definitions/cart.Person" }, + "ShippingGross": { + "description": "ShippingGross is the sum of all shipping costs including tax", + "$ref": "#/definitions/domain.Price" + }, + "ShippingGrossWithDiscounts": { + "description": "ShippingGrossWithDiscounts is the sum of all shipping costs with all shipping discounts including tax", + "$ref": "#/definitions/domain.Price" + }, + "ShippingNet": { + "description": "ShippingNet is the sum of all shipping costs", + "$ref": "#/definitions/domain.Price" + }, + "ShippingNetWithDiscounts": { + "description": "ShippingNetWithDiscounts is the sum of all shipping costs with all shipping discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalGross": { + "description": "SubTotalGross is the sum of all delivery subtotals (without shipping/ discounts)", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalGrossWithDiscounts": { + "description": "SubTotalGrossWithDiscounts is the sum of row gross prices reduced by the applied discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalNet": { + "description": "SubTotalNet is the sum of all delivery net subtotals (without shipping/ discounts)", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalNetWithDiscounts": { + "description": "SubTotalNetWithDiscounts is the sum of row net prices reduced by the net value of the applied discounts", + "$ref": "#/definitions/domain.Price" + }, + "TotalDiscountAmount": { + "description": "TotalDiscountAmount is the sum of all discounts (incl. shipping)", + "$ref": "#/definitions/domain.Price" + }, + "TotalGiftCardAmount": { + "description": "AppliedGiftCardsAmount is the part of GrandTotal which is paid by gift cards", + "$ref": "#/definitions/domain.Price" + }, "Totalitems": { "description": "Additional non taxable totals", "type": "array", @@ -1346,9 +1423,45 @@ "description": "DeliveryInfo contains details for this delivery e.g. how and where the delivery should be delivered to", "$ref": "#/definitions/cart.DeliveryInfo" }, + "GrandTotal": { + "description": "GrandTotal contains the final price to pay", + "$ref": "#/definitions/domain.Price" + }, + "ItemRelatedDiscountAmount": { + "description": "ItemRelatedDiscountAmount contains the sum of discounts that are related to the item, e.g. promo due to product attribute", + "$ref": "#/definitions/domain.Price" + }, + "NonItemRelatedDiscountAmount": { + "description": "NonItemRelatedDiscountAmount contains the sum of discounts that are not related to the item, e.g. a general promo", + "$ref": "#/definitions/domain.Price" + }, "ShippingItem": { "description": "ShippingItem\trepresent the shipping cost that may be involved in this delivery", "$ref": "#/definitions/cart.ShippingItem" + }, + "SubTotalDiscountAmount": { + "description": "TotalDiscountAmount contains the sum of all discounts (excl. shipping)", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalGross": { + "description": "SubTotalGross contains the sum of row gross prices, without shipping/discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalGrossWithDiscounts": { + "description": "SubTotalGrossWithDiscounts contains the sum of row gross prices reduced by the applied discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalNet": { + "description": "SubTotalNet contains the sum of row net prices, without shipping/discounts", + "$ref": "#/definitions/domain.Price" + }, + "SubTotalNetWithDiscounts": { + "description": "SubTotalNetWithDiscounts contains the sum of row net prices reduced by the net value of the applied discounts", + "$ref": "#/definitions/domain.Price" + }, + "TotalDiscountAmount": { + "description": "TotalDiscountAmount contains the sum of all discounts (incl. shipping)", + "$ref": "#/definitions/domain.Price" } } }, @@ -1428,7 +1541,7 @@ } }, "AppliedDiscounts": { - "description": "AppliedDiscounts contains the details about the discounts applied to this item - they can be \"itemrelated\" or not", + "description": "AppliedDiscounts contains the details about the discounts applied to this item - they can be \"itemrelated\" or not\nitemrelated would be e.g. special price, buy 3 pay 2\nnon-itemrelated would be e.g. 10% on everything", "type": "array", "items": { "$ref": "#/definitions/cart.AppliedDiscount" @@ -1442,9 +1555,18 @@ "description": "ID of the item - needs to be unique over the whole cart", "type": "string" }, + "ItemRelatedDiscountAmount": { + "description": "ItemRelatedDiscountAmount is the sum of all itemrelated Discounts", + "$ref": "#/definitions/domain.Price" + }, "MarketplaceCode": { + "description": "MarketplaceCode is the identifier for the product", "type": "string" }, + "NonItemRelatedDiscountAmount": { + "description": "NonItemRelatedDiscountAmount is the sum of non-itemrelated Discounts where IsItemRelated = false", + "$ref": "#/definitions/domain.Price" + }, "ProductName": { "type": "string" }, @@ -1452,38 +1574,61 @@ "type": "integer" }, "RowPriceGross": { - "description": "RowPriceGross", + "description": "RowPriceGross is the price incl. taxes for the whole Qty of products", + "$ref": "#/definitions/domain.Price" + }, + "RowPriceGrossWithDiscount": { + "description": "RowPriceGrossWithDiscount is the price incl. taxes with deducted discounts for the whole Qty of products\nThis is in most cases the final price for the customer to pay", + "$ref": "#/definitions/domain.Price" + }, + "RowPriceGrossWithItemRelatedDiscount": { + "description": "RowPriceGrossWithItemRelatedDiscount is the price incl. taxes with deducted item related discounts for the whole Qty of products", "$ref": "#/definitions/domain.Price" }, "RowPriceNet": { - "description": "RowPriceNet", + "description": "RowPriceNet is the price excl. taxes for the whole Qty of products", + "$ref": "#/definitions/domain.Price" + }, + "RowPriceNetWithDiscount": { + "description": "RowPriceNetWithDiscount is the discounted net price for the whole Qty of products", + "$ref": "#/definitions/domain.Price" + }, + "RowPriceNetWithItemRelatedDiscount": { + "description": "RowPriceNetWithItemRelatedDiscount is the price excl. taxes with deducted item related discounts for the whole Qty of products", "$ref": "#/definitions/domain.Price" }, "RowTaxes": { - "description": "RowPriceGross", + "description": "RowTaxes is a list of all taxes applied for the given Qty of products", "type": "array", "items": { "$ref": "#/definitions/cart.Tax" } }, "SinglePriceGross": { - "description": "SinglePriceGross gross price (incl. taxes) for a single product", + "description": "SinglePriceGross is the gross price (incl. taxes) for a single product", "$ref": "#/definitions/domain.Price" }, "SinglePriceNet": { - "description": "SinglePriceNet net price (excl. taxes) for a single product", + "description": "SinglePriceNet is the net price (excl. taxes) for a single product", "$ref": "#/definitions/domain.Price" }, "SourceID": { "description": "Source Id of where the items should be initial picked - This is set by the SourcingLogic", "type": "string" }, + "TotalDiscountAmount": { + "description": "TotalDiscountAmount is the sum of all applied discounts (aka the savings for the customer)", + "$ref": "#/definitions/domain.Price" + }, "VariantMarketPlaceCode": { "description": "VariantMarketPlaceCode is used for Configurable products", "type": "string" } } }, + "cart.PaymentSelection": { + "type": "object" + }, "cart.Person": { "type": "object", "properties": { @@ -1528,9 +1673,15 @@ "PriceGross": { "$ref": "#/definitions/domain.Price" }, + "PriceGrossWithDiscounts": { + "$ref": "#/definitions/domain.Price" + }, "PriceNet": { "$ref": "#/definitions/domain.Price" }, + "PriceNetWithDiscounts": { + "$ref": "#/definitions/domain.Price" + }, "TaxAmount": { "$ref": "#/definitions/domain.Price" }, @@ -1587,17 +1738,6 @@ } } }, - "cartResultError": { - "type": "object", - "properties": { - "Code": { - "type": "string" - }, - "Message": { - "type": "string" - } - } - }, "checkoutError": { "type": "object", "properties": { @@ -1613,9 +1753,11 @@ "type": "object", "properties": { "Error": { - "$ref": "#/definitions/cartResultError" + "$ref": "#/definitions/paymentResultError" + }, + "Product": { + "$ref": "#/definitions/domain.BasicProduct" }, - "Product": {}, "Success": { "type": "boolean" } @@ -1630,13 +1772,15 @@ "CartValidationResult": { "$ref": "#/definitions/validation.Result" }, - "Data": {}, + "Data": { + "type": "object" + }, "DataValidationInfo": { "type": "object" }, "Error": { "description": "Contains details if success is false", - "$ref": "#/definitions/cartResultError" + "$ref": "#/definitions/paymentResultError" }, "Success": { "type": "boolean" @@ -1672,7 +1816,9 @@ "State": { "type": "string" }, - "StateData": {}, + "StateData": { + "$ref": "#/definitions/process.StateData" + }, "UUID": { "type": "string" } @@ -1729,7 +1875,9 @@ "Item": { "$ref": "#/definitions/cart.Item" }, - "Product": {} + "Product": { + "$ref": "#/definitions/domain.BasicProduct" + } } }, "decorator.DecoratedDelivery": { @@ -1752,7 +1900,9 @@ "Label": { "type": "string" }, - "RawValue": {} + "RawValue": { + "type": "object" + } } }, "domain.Badge": { @@ -1766,6 +1916,9 @@ } } }, + "domain.BasicProduct": { + "type": "object" + }, "domain.CategoryTeaser": { "type": "object", "properties": { @@ -1827,7 +1980,8 @@ "$ref": "#/definitions/domain.FlowActionData" }, "Data": { - "description": "Data contains additional information related to the action / flow" + "description": "Data contains additional information related to the action / flow", + "type": "object" }, "Error": { "description": "Error contains additional information in case of an error (e.g. payment failed)", @@ -2048,7 +2202,9 @@ }, "Media": { "type": "array", - "items": {} + "items": { + "$ref": "#/definitions/ProductMedia" + } }, "RetailerCode": { "type": "string" @@ -2105,7 +2261,9 @@ "Media": { "description": "Media", "type": "array", - "items": {} + "items": { + "$ref": "#/definitions/ProductMedia" + } }, "PreSelectedVariantSku": { "description": "PreSelectedVariantSku might be set for configurables to give a hint to link to a variant of a configurable (That might be the case if a user filters for an attribute and in the teaser the variant with that attribute is shown)", @@ -2155,6 +2313,17 @@ } } }, + "paymentResultError": { + "type": "object", + "properties": { + "Code": { + "type": "string" + }, + "Message": { + "type": "string" + } + } + }, "placeorder.CreditCardInfo": { "type": "object", "properties": { @@ -2183,6 +2352,9 @@ } } }, + "process.StateData": { + "type": "object" + }, "validation.ItemValidationError": { "type": "object", "properties": { diff --git a/docs/openapi/swagger.yaml b/docs/openapi/swagger.yaml index b917858da..5b9becaa4 100644 --- a/docs/openapi/swagger.yaml +++ b/docs/openapi/swagger.yaml @@ -14,6 +14,19 @@ definitions: additionalProperties: $ref: '#/definitions/CategoryAttribute' type: object + ProductMedia: + properties: + MimeType: + type: string + Reference: + type: string + Title: + type: string + Type: + type: string + Usage: + type: string + type: object application.PlaceOrderPaymentInfo: properties: Amount: @@ -161,16 +174,70 @@ definitions: EntityID: description: EntityID is a second identifier that may be used by some backends type: string + GrandTotal: + $ref: '#/definitions/domain.Price' + description: GrandTotal is the final amount that need to be paid by the customer + (gross) + GrandTotalWithGiftCards: + $ref: '#/definitions/domain.Price' + description: GrandTotalWithGiftCards is the final amount with the applied + gift cards subtracted. ID: description: ID is the main identifier of the cart type: string + ItemRelatedDiscountAmount: + $ref: '#/definitions/domain.Price' + description: ItemRelatedDiscountAmount is the sum of discounts that are related + to the item (including shipping discounts) + NonItemRelatedDiscountAmount: + $ref: '#/definitions/domain.Price' + description: NonItemRelatedDiscountAmount is the sum of discounts that are + not related to the item (including shipping discounts) PaymentSelection: + $ref: '#/definitions/cart.PaymentSelection' description: PaymentSelection is used to store information on "how" the customer wants to pay Purchaser: $ref: '#/definitions/cart.Person' description: Purchaser hold additional infos for the legal contact person in this order + ShippingGross: + $ref: '#/definitions/domain.Price' + description: ShippingGross is the sum of all shipping costs including tax + ShippingGrossWithDiscounts: + $ref: '#/definitions/domain.Price' + description: ShippingGrossWithDiscounts is the sum of all shipping costs with + all shipping discounts including tax + ShippingNet: + $ref: '#/definitions/domain.Price' + description: ShippingNet is the sum of all shipping costs + ShippingNetWithDiscounts: + $ref: '#/definitions/domain.Price' + description: ShippingNetWithDiscounts is the sum of all shipping costs with + all shipping discounts + SubTotalGross: + $ref: '#/definitions/domain.Price' + description: SubTotalGross is the sum of all delivery subtotals (without shipping/ + discounts) + SubTotalGrossWithDiscounts: + $ref: '#/definitions/domain.Price' + description: SubTotalGrossWithDiscounts is the sum of row gross prices reduced + by the applied discounts + SubTotalNet: + $ref: '#/definitions/domain.Price' + description: SubTotalNet is the sum of all delivery net subtotals (without + shipping/ discounts) + SubTotalNetWithDiscounts: + $ref: '#/definitions/domain.Price' + description: SubTotalNetWithDiscounts is the sum of row net prices reduced + by the net value of the applied discounts + TotalDiscountAmount: + $ref: '#/definitions/domain.Price' + description: TotalDiscountAmount is the sum of all discounts (incl. shipping) + TotalGiftCardAmount: + $ref: '#/definitions/domain.Price' + description: AppliedGiftCardsAmount is the part of GrandTotal which is paid + by gift cards Totalitems: description: Additional non taxable totals items: @@ -198,10 +265,43 @@ definitions: $ref: '#/definitions/cart.DeliveryInfo' description: DeliveryInfo contains details for this delivery e.g. how and where the delivery should be delivered to + GrandTotal: + $ref: '#/definitions/domain.Price' + description: GrandTotal contains the final price to pay + ItemRelatedDiscountAmount: + $ref: '#/definitions/domain.Price' + description: ItemRelatedDiscountAmount contains the sum of discounts that + are related to the item, e.g. promo due to product attribute + NonItemRelatedDiscountAmount: + $ref: '#/definitions/domain.Price' + description: NonItemRelatedDiscountAmount contains the sum of discounts that + are not related to the item, e.g. a general promo ShippingItem: $ref: '#/definitions/cart.ShippingItem' description: "ShippingItem\trepresent the shipping cost that may be involved in this delivery" + SubTotalDiscountAmount: + $ref: '#/definitions/domain.Price' + description: TotalDiscountAmount contains the sum of all discounts (excl. + shipping) + SubTotalGross: + $ref: '#/definitions/domain.Price' + description: SubTotalGross contains the sum of row gross prices, without shipping/discounts + SubTotalGrossWithDiscounts: + $ref: '#/definitions/domain.Price' + description: SubTotalGrossWithDiscounts contains the sum of row gross prices + reduced by the applied discounts + SubTotalNet: + $ref: '#/definitions/domain.Price' + description: SubTotalNet contains the sum of row net prices, without shipping/discounts + SubTotalNetWithDiscounts: + $ref: '#/definitions/domain.Price' + description: SubTotalNetWithDiscounts contains the sum of row net prices reduced + by the net value of the applied discounts + TotalDiscountAmount: + $ref: '#/definitions/domain.Price' + description: TotalDiscountAmount contains the sum of all discounts (incl. + shipping) type: object cart.DeliveryInfo: properties: @@ -266,8 +366,10 @@ definitions: type: string type: object AppliedDiscounts: - description: AppliedDiscounts contains the details about the discounts applied - to this item - they can be "itemrelated" or not + description: |- + AppliedDiscounts contains the details about the discounts applied to this item - they can be "itemrelated" or not + itemrelated would be e.g. special price, buy 3 pay 2 + non-itemrelated would be e.g. 10% on everything items: $ref: '#/definitions/cart.AppliedDiscount' type: array @@ -279,37 +381,70 @@ definitions: ID: description: ID of the item - needs to be unique over the whole cart type: string + ItemRelatedDiscountAmount: + $ref: '#/definitions/domain.Price' + description: ItemRelatedDiscountAmount is the sum of all itemrelated Discounts MarketplaceCode: + description: MarketplaceCode is the identifier for the product type: string + NonItemRelatedDiscountAmount: + $ref: '#/definitions/domain.Price' + description: NonItemRelatedDiscountAmount is the sum of non-itemrelated Discounts + where IsItemRelated = false ProductName: type: string Qty: type: integer RowPriceGross: $ref: '#/definitions/domain.Price' - description: RowPriceGross + description: RowPriceGross is the price incl. taxes for the whole Qty of products + RowPriceGrossWithDiscount: + $ref: '#/definitions/domain.Price' + description: |- + RowPriceGrossWithDiscount is the price incl. taxes with deducted discounts for the whole Qty of products + This is in most cases the final price for the customer to pay + RowPriceGrossWithItemRelatedDiscount: + $ref: '#/definitions/domain.Price' + description: RowPriceGrossWithItemRelatedDiscount is the price incl. taxes + with deducted item related discounts for the whole Qty of products RowPriceNet: $ref: '#/definitions/domain.Price' - description: RowPriceNet + description: RowPriceNet is the price excl. taxes for the whole Qty of products + RowPriceNetWithDiscount: + $ref: '#/definitions/domain.Price' + description: RowPriceNetWithDiscount is the discounted net price for the whole + Qty of products + RowPriceNetWithItemRelatedDiscount: + $ref: '#/definitions/domain.Price' + description: RowPriceNetWithItemRelatedDiscount is the price excl. taxes with + deducted item related discounts for the whole Qty of products RowTaxes: - description: RowPriceGross + description: RowTaxes is a list of all taxes applied for the given Qty of + products items: $ref: '#/definitions/cart.Tax' type: array SinglePriceGross: $ref: '#/definitions/domain.Price' - description: SinglePriceGross gross price (incl. taxes) for a single product + description: SinglePriceGross is the gross price (incl. taxes) for a single + product SinglePriceNet: $ref: '#/definitions/domain.Price' - description: SinglePriceNet net price (excl. taxes) for a single product + description: SinglePriceNet is the net price (excl. taxes) for a single product SourceID: description: Source Id of where the items should be initial picked - This is set by the SourcingLogic type: string + TotalDiscountAmount: + $ref: '#/definitions/domain.Price' + description: TotalDiscountAmount is the sum of all applied discounts (aka + the savings for the customer) VariantMarketPlaceCode: description: VariantMarketPlaceCode is used for Configurable products type: string type: object + cart.PaymentSelection: + type: object cart.Person: properties: Address: @@ -340,8 +475,12 @@ definitions: type: array PriceGross: $ref: '#/definitions/domain.Price' + PriceGrossWithDiscounts: + $ref: '#/definitions/domain.Price' PriceNet: $ref: '#/definitions/domain.Price' + PriceNetWithDiscounts: + $ref: '#/definitions/domain.Price' TaxAmount: $ref: '#/definitions/domain.Price' Title: @@ -378,13 +517,6 @@ definitions: Type: type: string type: object - cartResultError: - properties: - Code: - type: string - Message: - type: string - type: object checkoutError: properties: Code: @@ -395,8 +527,9 @@ definitions: controller.APIResult: properties: Error: - $ref: '#/definitions/cartResultError' - Product: {} + $ref: '#/definitions/paymentResultError' + Product: + $ref: '#/definitions/domain.BasicProduct' Success: type: boolean type: object @@ -406,11 +539,12 @@ definitions: $ref: '#/definitions/cart.Teaser' CartValidationResult: $ref: '#/definitions/validation.Result' - Data: {} + Data: + type: object DataValidationInfo: type: object Error: - $ref: '#/definitions/cartResultError' + $ref: '#/definitions/paymentResultError' description: Contains details if success is false Success: type: boolean @@ -434,7 +568,8 @@ definitions: $ref: '#/definitions/controller.placedOrderInfos' State: type: string - StateData: {} + StateData: + $ref: '#/definitions/process.StateData' UUID: type: string type: object @@ -471,7 +606,8 @@ definitions: properties: Item: $ref: '#/definitions/cart.Item' - Product: {} + Product: + $ref: '#/definitions/domain.BasicProduct' type: object decorator.DecoratedDelivery: properties: @@ -486,7 +622,8 @@ definitions: properties: Label: type: string - RawValue: {} + RawValue: + type: object type: object domain.Badge: properties: @@ -495,6 +632,8 @@ definitions: Label: type: string type: object + domain.BasicProduct: + type: object domain.CategoryTeaser: properties: Code: @@ -543,6 +682,7 @@ definitions: Data: description: Data contains additional information related to the action / flow + type: object Error: $ref: '#/definitions/domain.Error' description: Error contains additional information in case of an error (e.g. @@ -692,7 +832,8 @@ definitions: MarketPlaceCode: type: string Media: - items: {} + items: + $ref: '#/definitions/ProductMedia' type: array RetailerCode: type: string @@ -731,7 +872,8 @@ definitions: type: string Media: description: Media - items: {} + items: + $ref: '#/definitions/ProductMedia' type: array PreSelectedVariantSku: description: PreSelectedVariantSku might be set for configurables to give @@ -772,6 +914,13 @@ definitions: UsedPaymentMethod: type: string type: object + paymentResultError: + properties: + Code: + type: string + Message: + type: string + type: object placeorder.CreditCardInfo: properties: AnonymizedCardNumber: @@ -790,6 +939,8 @@ definitions: OrderNumber: type: string type: object + process.StateData: + type: object validation.ItemValidationError: properties: ErrorMessageKey: @@ -1484,7 +1635,7 @@ paths: "500": description: Internal Server Error schema: - $ref: '#/definitions/cartResultError' + $ref: '#/definitions/paymentResultError' summary: Get the payment status of current cart (or last placed cart) tags: - Payment diff --git a/payment/Changelog.md b/payment/Changelog.md deleted file mode 100644 index 7a63fa69c..000000000 --- a/payment/Changelog.md +++ /dev/null @@ -1,22 +0,0 @@ -# 30. September 2019 -* Enhance WebCartPaymentGateway with CancelOrderPayment - -# 20. August 2019 -* Reworked WebCartPaymentGateway Interface - * Removed StartWebFlow func - * Renamed GetFlowResult to OrderPaymentFromFlow - * Add function to get newly introduced FlowStatus -* Add new generic API endpoint to fetch the current FlowStatus of a payment -* Enhanced / updated domain model - * Changed FlowResult to contain FlowStatus and allow flag to represent and early place order - * Add FlowStatus which contains current status of the payment flow - -# 20. February 2020 -* Add `PaymentService` to easily work with bound PaymentGateway's - * `PaymentService.AvailablePaymentGateways()` returns all bound gateways - * `PaymentService.PaymentGateway()` gets the payment gateway by gateway code - * `PaymentService.PaymentGatewayByCart()` gets the payment gateway of the cart payment selection - -* Extend the `FlowStatus` struct with more standardized `FlowActionData` -* Add standardized Flow Actions `PaymentFlowActionShowIframe`, `PaymentFlowActionShowHTML`, `PaymentFlowActionRedirect`, - `PaymentFlowActionPostRedirect` please use these in your payment adapter since the standard place order relies on them. \ No newline at end of file diff --git a/search/Changelog.md b/search/Changelog.md deleted file mode 100644 index 15610e330..000000000 --- a/search/Changelog.md +++ /dev/null @@ -1,2 +0,0 @@ -# 20. February 2020 -* Extend `Suggestion` struct with `Type` and `AdditionalAttributes` to be able to distinguish between product/category suggestions \ No newline at end of file diff --git a/test/integrationtest/projecttest/graphql/generated.go b/test/integrationtest/projecttest/graphql/generated.go index 33a2c1069..b3533806a 100644 --- a/test/integrationtest/projecttest/graphql/generated.go +++ b/test/integrationtest/projecttest/graphql/generated.go @@ -154,54 +154,54 @@ type ComplexityRoot struct { } CommerceCartCart struct { - AdditionalData func(childComplexity int) int - AllShippingTitles func(childComplexity int) int - AppliedCouponCodes func(childComplexity int) int - AppliedGiftCards func(childComplexity int) int - AuthenticatedUserID func(childComplexity int) int - BelongsToAuthenticatedUser func(childComplexity int) int - BillingAddress func(childComplexity int) int - DefaultCurrency func(childComplexity int) int - Deliveries func(childComplexity int) int - EntityID func(childComplexity int) int - GetByExternalReference func(childComplexity int, ref string) int - GetByItemID func(childComplexity int, itemID string) int - GetCartTeaser func(childComplexity int) int - GetDeliveryByCode func(childComplexity int, deliveryCode string) int - GetDeliveryByItemID func(childComplexity int, itemID string) int - GetDeliveryCodes func(childComplexity int) int - GetMainShippingEMail func(childComplexity int) int - GetPaymentReference func(childComplexity int) int - GetTotalItemsByType func(childComplexity int, typeCode string) int - GetTotalQty func(childComplexity int, marketPlaceCode string, variantCode string) int - GetVoucherSavings func(childComplexity int) int - GrandTotal func(childComplexity int) int - GrandTotalCharges func(childComplexity int) int - HasAppliedCouponCode func(childComplexity int) int - HasAppliedGiftCards func(childComplexity int) int - HasDeliveryForCode func(childComplexity int, deliveryCode string) int - HasRemainingGiftCards func(childComplexity int) int - HasShippingCosts func(childComplexity int) int - ID func(childComplexity int) int - IsEmpty func(childComplexity int) int - IsPaymentSelected func(childComplexity int) int - ItemCount func(childComplexity int) int - PaymentSelection func(childComplexity int) int - ProductCount func(childComplexity int) int - Purchaser func(childComplexity int) int - SubTotalGross func(childComplexity int) int - SubTotalGrossWithDiscounts func(childComplexity int) int - SubTotalNet func(childComplexity int) int - SubTotalNetWithDiscounts func(childComplexity int) int - SumItemRelatedDiscountAmount func(childComplexity int) int - SumNonItemRelatedDiscountAmount func(childComplexity int) int - SumShippingGross func(childComplexity int) int - SumShippingGrossWithDiscounts func(childComplexity int) int - SumShippingNet func(childComplexity int) int - SumShippingNetWithDiscounts func(childComplexity int) int - SumTotalDiscountAmount func(childComplexity int) int - SumTotalTaxAmount func(childComplexity int) int - Totalitems func(childComplexity int) int + AdditionalData func(childComplexity int) int + AllShippingTitles func(childComplexity int) int + AppliedCouponCodes func(childComplexity int) int + AppliedGiftCards func(childComplexity int) int + AuthenticatedUserID func(childComplexity int) int + BelongsToAuthenticatedUser func(childComplexity int) int + BillingAddress func(childComplexity int) int + DefaultCurrency func(childComplexity int) int + Deliveries func(childComplexity int) int + EntityID func(childComplexity int) int + GetByExternalReference func(childComplexity int, ref string) int + GetByItemID func(childComplexity int, itemID string) int + GetCartTeaser func(childComplexity int) int + GetDeliveryByCode func(childComplexity int, deliveryCode string) int + GetDeliveryByItemID func(childComplexity int, itemID string) int + GetDeliveryCodes func(childComplexity int) int + GetMainShippingEMail func(childComplexity int) int + GetPaymentReference func(childComplexity int) int + GetTotalItemsByType func(childComplexity int, typeCode string) int + GetTotalQty func(childComplexity int, marketPlaceCode string, variantCode string) int + GetVoucherSavings func(childComplexity int) int + GrandTotal func(childComplexity int) int + GrandTotalCharges func(childComplexity int) int + HasAppliedCouponCode func(childComplexity int) int + HasAppliedGiftCards func(childComplexity int) int + HasDeliveryForCode func(childComplexity int, deliveryCode string) int + HasRemainingGiftCards func(childComplexity int) int + HasShippingCosts func(childComplexity int) int + ID func(childComplexity int) int + IsEmpty func(childComplexity int) int + IsPaymentSelected func(childComplexity int) int + ItemCount func(childComplexity int) int + ItemRelatedDiscountAmount func(childComplexity int) int + NonItemRelatedDiscountAmount func(childComplexity int) int + PaymentSelection func(childComplexity int) int + ProductCount func(childComplexity int) int + Purchaser func(childComplexity int) int + ShippingGross func(childComplexity int) int + ShippingGrossWithDiscounts func(childComplexity int) int + ShippingNet func(childComplexity int) int + ShippingNetWithDiscounts func(childComplexity int) int + SubTotalGross func(childComplexity int) int + SubTotalGrossWithDiscounts func(childComplexity int) int + SubTotalNet func(childComplexity int) int + SubTotalNetWithDiscounts func(childComplexity int) int + SumTotalTaxAmount func(childComplexity int) int + TotalDiscountAmount func(childComplexity int) int + Totalitems func(childComplexity int) int } CommerceCartCouponCode struct { @@ -237,19 +237,19 @@ type ComplexityRoot struct { } CommerceCartDelivery struct { - Cartitems func(childComplexity int) int - DeliveryInfo func(childComplexity int) int - GrandTotal func(childComplexity int) int - HasItems func(childComplexity int) int - ShippingItem func(childComplexity int) int - SubTotalGross func(childComplexity int) int - SubTotalGrossWithDiscounts func(childComplexity int) int - SubTotalNet func(childComplexity int) int - SubTotalNetWithDiscounts func(childComplexity int) int - SumItemRelatedDiscountAmount func(childComplexity int) int - SumNonItemRelatedDiscountAmount func(childComplexity int) int - SumTotalDiscountAmount func(childComplexity int) int - SumTotalTaxAmount func(childComplexity int) int + Cartitems func(childComplexity int) int + DeliveryInfo func(childComplexity int) int + GrandTotal func(childComplexity int) int + HasItems func(childComplexity int) int + ItemRelatedDiscountAmount func(childComplexity int) int + NonItemRelatedDiscountAmount func(childComplexity int) int + ShippingItem func(childComplexity int) int + SubTotalGross func(childComplexity int) int + SubTotalGrossWithDiscounts func(childComplexity int) int + SubTotalNet func(childComplexity int) int + SubTotalNetWithDiscounts func(childComplexity int) int + SumTotalTaxAmount func(childComplexity int) int + TotalDiscountAmount func(childComplexity int) int } CommerceCartDeliveryAddressForm struct { @@ -403,13 +403,13 @@ type ComplexityRoot struct { CommerceCartSummary struct { Discounts func(childComplexity int) int + GrandTotalWithGiftCards func(childComplexity int) int HasAppliedDiscounts func(childComplexity int) int - SumAppliedDiscounts func(childComplexity int) int - SumAppliedGiftCards func(childComplexity int) int - SumGrandTotalWithGiftCards func(childComplexity int) int SumPaymentSelectionCartSplitValueAmountByMethods func(childComplexity int, methods []string) int SumTaxes func(childComplexity int) int SumTotalDiscountWithGiftCardsAmount func(childComplexity int) int + TotalDiscountAmount func(childComplexity int) int + TotalGiftCardAmount func(childComplexity int) int } CommerceCartTax struct { @@ -1710,6 +1710,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CommerceCartCart.ItemCount(childComplexity), true + case "Commerce_Cart_Cart.itemRelatedDiscountAmount": + if e.complexity.CommerceCartCart.ItemRelatedDiscountAmount == nil { + break + } + + return e.complexity.CommerceCartCart.ItemRelatedDiscountAmount(childComplexity), true + + case "Commerce_Cart_Cart.nonItemRelatedDiscountAmount": + if e.complexity.CommerceCartCart.NonItemRelatedDiscountAmount == nil { + break + } + + return e.complexity.CommerceCartCart.NonItemRelatedDiscountAmount(childComplexity), true + case "Commerce_Cart_Cart.paymentSelection": if e.complexity.CommerceCartCart.PaymentSelection == nil { break @@ -1731,89 +1745,75 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CommerceCartCart.Purchaser(childComplexity), true - case "Commerce_Cart_Cart.subTotalGross": - if e.complexity.CommerceCartCart.SubTotalGross == nil { + case "Commerce_Cart_Cart.shippingGross": + if e.complexity.CommerceCartCart.ShippingGross == nil { break } - return e.complexity.CommerceCartCart.SubTotalGross(childComplexity), true + return e.complexity.CommerceCartCart.ShippingGross(childComplexity), true - case "Commerce_Cart_Cart.subTotalGrossWithDiscounts": - if e.complexity.CommerceCartCart.SubTotalGrossWithDiscounts == nil { + case "Commerce_Cart_Cart.shippingGrossWithDiscounts": + if e.complexity.CommerceCartCart.ShippingGrossWithDiscounts == nil { break } - return e.complexity.CommerceCartCart.SubTotalGrossWithDiscounts(childComplexity), true + return e.complexity.CommerceCartCart.ShippingGrossWithDiscounts(childComplexity), true - case "Commerce_Cart_Cart.subTotalNet": - if e.complexity.CommerceCartCart.SubTotalNet == nil { + case "Commerce_Cart_Cart.shippingNet": + if e.complexity.CommerceCartCart.ShippingNet == nil { break } - return e.complexity.CommerceCartCart.SubTotalNet(childComplexity), true + return e.complexity.CommerceCartCart.ShippingNet(childComplexity), true - case "Commerce_Cart_Cart.subTotalNetWithDiscounts": - if e.complexity.CommerceCartCart.SubTotalNetWithDiscounts == nil { - break - } - - return e.complexity.CommerceCartCart.SubTotalNetWithDiscounts(childComplexity), true - - case "Commerce_Cart_Cart.sumItemRelatedDiscountAmount": - if e.complexity.CommerceCartCart.SumItemRelatedDiscountAmount == nil { - break - } - - return e.complexity.CommerceCartCart.SumItemRelatedDiscountAmount(childComplexity), true - - case "Commerce_Cart_Cart.sumNonItemRelatedDiscountAmount": - if e.complexity.CommerceCartCart.SumNonItemRelatedDiscountAmount == nil { + case "Commerce_Cart_Cart.shippingNetWithDiscounts": + if e.complexity.CommerceCartCart.ShippingNetWithDiscounts == nil { break } - return e.complexity.CommerceCartCart.SumNonItemRelatedDiscountAmount(childComplexity), true + return e.complexity.CommerceCartCart.ShippingNetWithDiscounts(childComplexity), true - case "Commerce_Cart_Cart.sumShippingGross": - if e.complexity.CommerceCartCart.SumShippingGross == nil { + case "Commerce_Cart_Cart.subTotalGross": + if e.complexity.CommerceCartCart.SubTotalGross == nil { break } - return e.complexity.CommerceCartCart.SumShippingGross(childComplexity), true + return e.complexity.CommerceCartCart.SubTotalGross(childComplexity), true - case "Commerce_Cart_Cart.sumShippingGrossWithDiscounts": - if e.complexity.CommerceCartCart.SumShippingGrossWithDiscounts == nil { + case "Commerce_Cart_Cart.subTotalGrossWithDiscounts": + if e.complexity.CommerceCartCart.SubTotalGrossWithDiscounts == nil { break } - return e.complexity.CommerceCartCart.SumShippingGrossWithDiscounts(childComplexity), true + return e.complexity.CommerceCartCart.SubTotalGrossWithDiscounts(childComplexity), true - case "Commerce_Cart_Cart.sumShippingNet": - if e.complexity.CommerceCartCart.SumShippingNet == nil { + case "Commerce_Cart_Cart.subTotalNet": + if e.complexity.CommerceCartCart.SubTotalNet == nil { break } - return e.complexity.CommerceCartCart.SumShippingNet(childComplexity), true + return e.complexity.CommerceCartCart.SubTotalNet(childComplexity), true - case "Commerce_Cart_Cart.sumShippingNetWithDiscounts": - if e.complexity.CommerceCartCart.SumShippingNetWithDiscounts == nil { + case "Commerce_Cart_Cart.subTotalNetWithDiscounts": + if e.complexity.CommerceCartCart.SubTotalNetWithDiscounts == nil { break } - return e.complexity.CommerceCartCart.SumShippingNetWithDiscounts(childComplexity), true + return e.complexity.CommerceCartCart.SubTotalNetWithDiscounts(childComplexity), true - case "Commerce_Cart_Cart.sumTotalDiscountAmount": - if e.complexity.CommerceCartCart.SumTotalDiscountAmount == nil { + case "Commerce_Cart_Cart.sumTotalTaxAmount": + if e.complexity.CommerceCartCart.SumTotalTaxAmount == nil { break } - return e.complexity.CommerceCartCart.SumTotalDiscountAmount(childComplexity), true + return e.complexity.CommerceCartCart.SumTotalTaxAmount(childComplexity), true - case "Commerce_Cart_Cart.sumTotalTaxAmount": - if e.complexity.CommerceCartCart.SumTotalTaxAmount == nil { + case "Commerce_Cart_Cart.totalDiscountAmount": + if e.complexity.CommerceCartCart.TotalDiscountAmount == nil { break } - return e.complexity.CommerceCartCart.SumTotalTaxAmount(childComplexity), true + return e.complexity.CommerceCartCart.TotalDiscountAmount(childComplexity), true case "Commerce_Cart_Cart.totalitems": if e.complexity.CommerceCartCart.Totalitems == nil { @@ -1958,6 +1958,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CommerceCartDelivery.HasItems(childComplexity), true + case "Commerce_Cart_Delivery.itemRelatedDiscountAmount": + if e.complexity.CommerceCartDelivery.ItemRelatedDiscountAmount == nil { + break + } + + return e.complexity.CommerceCartDelivery.ItemRelatedDiscountAmount(childComplexity), true + + case "Commerce_Cart_Delivery.nonItemRelatedDiscountAmount": + if e.complexity.CommerceCartDelivery.NonItemRelatedDiscountAmount == nil { + break + } + + return e.complexity.CommerceCartDelivery.NonItemRelatedDiscountAmount(childComplexity), true + case "Commerce_Cart_Delivery.shippingItem": if e.complexity.CommerceCartDelivery.ShippingItem == nil { break @@ -1993,33 +2007,19 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CommerceCartDelivery.SubTotalNetWithDiscounts(childComplexity), true - case "Commerce_Cart_Delivery.sumItemRelatedDiscountAmount": - if e.complexity.CommerceCartDelivery.SumItemRelatedDiscountAmount == nil { - break - } - - return e.complexity.CommerceCartDelivery.SumItemRelatedDiscountAmount(childComplexity), true - - case "Commerce_Cart_Delivery.sumNonItemRelatedDiscountAmount": - if e.complexity.CommerceCartDelivery.SumNonItemRelatedDiscountAmount == nil { - break - } - - return e.complexity.CommerceCartDelivery.SumNonItemRelatedDiscountAmount(childComplexity), true - - case "Commerce_Cart_Delivery.sumTotalDiscountAmount": - if e.complexity.CommerceCartDelivery.SumTotalDiscountAmount == nil { + case "Commerce_Cart_Delivery.sumTotalTaxAmount": + if e.complexity.CommerceCartDelivery.SumTotalTaxAmount == nil { break } - return e.complexity.CommerceCartDelivery.SumTotalDiscountAmount(childComplexity), true + return e.complexity.CommerceCartDelivery.SumTotalTaxAmount(childComplexity), true - case "Commerce_Cart_Delivery.sumTotalTaxAmount": - if e.complexity.CommerceCartDelivery.SumTotalTaxAmount == nil { + case "Commerce_Cart_Delivery.totalDiscountAmount": + if e.complexity.CommerceCartDelivery.TotalDiscountAmount == nil { break } - return e.complexity.CommerceCartDelivery.SumTotalTaxAmount(childComplexity), true + return e.complexity.CommerceCartDelivery.TotalDiscountAmount(childComplexity), true case "Commerce_Cart_DeliveryAddressForm.carrier": if e.complexity.CommerceCartDeliveryAddressForm.Carrier == nil { @@ -2619,33 +2619,19 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CommerceCartSummary.Discounts(childComplexity), true - case "Commerce_Cart_Summary.hasAppliedDiscounts": - if e.complexity.CommerceCartSummary.HasAppliedDiscounts == nil { + case "Commerce_Cart_Summary.grandTotalWithGiftCards": + if e.complexity.CommerceCartSummary.GrandTotalWithGiftCards == nil { break } - return e.complexity.CommerceCartSummary.HasAppliedDiscounts(childComplexity), true - - case "Commerce_Cart_Summary.sumAppliedDiscounts": - if e.complexity.CommerceCartSummary.SumAppliedDiscounts == nil { - break - } + return e.complexity.CommerceCartSummary.GrandTotalWithGiftCards(childComplexity), true - return e.complexity.CommerceCartSummary.SumAppliedDiscounts(childComplexity), true - - case "Commerce_Cart_Summary.sumAppliedGiftCards": - if e.complexity.CommerceCartSummary.SumAppliedGiftCards == nil { - break - } - - return e.complexity.CommerceCartSummary.SumAppliedGiftCards(childComplexity), true - - case "Commerce_Cart_Summary.sumGrandTotalWithGiftCards": - if e.complexity.CommerceCartSummary.SumGrandTotalWithGiftCards == nil { + case "Commerce_Cart_Summary.hasAppliedDiscounts": + if e.complexity.CommerceCartSummary.HasAppliedDiscounts == nil { break } - return e.complexity.CommerceCartSummary.SumGrandTotalWithGiftCards(childComplexity), true + return e.complexity.CommerceCartSummary.HasAppliedDiscounts(childComplexity), true case "Commerce_Cart_Summary.sumPaymentSelectionCartSplitValueAmountByMethods": if e.complexity.CommerceCartSummary.SumPaymentSelectionCartSplitValueAmountByMethods == nil { @@ -2673,6 +2659,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CommerceCartSummary.SumTotalDiscountWithGiftCardsAmount(childComplexity), true + case "Commerce_Cart_Summary.totalDiscountAmount": + if e.complexity.CommerceCartSummary.TotalDiscountAmount == nil { + break + } + + return e.complexity.CommerceCartSummary.TotalDiscountAmount(childComplexity), true + + case "Commerce_Cart_Summary.totalGiftCardAmount": + if e.complexity.CommerceCartSummary.TotalGiftCardAmount == nil { + break + } + + return e.complexity.CommerceCartSummary.TotalGiftCardAmount(childComplexity), true + case "Commerce_Cart_Tax.amount": if e.complexity.CommerceCartTax.Amount == nil { break @@ -5702,9 +5702,9 @@ extend type Query { type Commerce_Cart_Summary { discounts: Commerce_Cart_AppliedDiscounts! - sumAppliedDiscounts: Commerce_Price - sumAppliedGiftCards: Commerce_Price - sumGrandTotalWithGiftCards: Commerce_Price + totalDiscountAmount: Commerce_Price + totalGiftCardAmount: Commerce_Price + grandTotalWithGiftCards: Commerce_Price sumTotalDiscountWithGiftCardsAmount: Commerce_Price hasAppliedDiscounts: Boolean! sumTaxes: Commerce_Cart_Taxes @@ -5746,10 +5746,10 @@ type Commerce_Cart_Cart { getVoucherSavings: Commerce_Price! getCartTeaser: Commerce_Cart_Teaser! - sumShippingNet: Commerce_Price! - sumShippingNetWithDiscounts: Commerce_Price! - sumShippingGross: Commerce_Price! - sumShippingGrossWithDiscounts: Commerce_Price! + shippingNet: Commerce_Price! + shippingNetWithDiscounts: Commerce_Price! + shippingGross: Commerce_Price! + shippingGrossWithDiscounts: Commerce_Price! hasShippingCosts: Boolean! allShippingTitles: [String!] @@ -5757,9 +5757,9 @@ type Commerce_Cart_Cart { subTotalGross: Commerce_Price! subTotalGrossWithDiscounts: Commerce_Price! subTotalNetWithDiscounts: Commerce_Price! - sumTotalDiscountAmount: Commerce_Price! - sumNonItemRelatedDiscountAmount: Commerce_Price! - sumItemRelatedDiscountAmount: Commerce_Price! + totalDiscountAmount: Commerce_Price! + nonItemRelatedDiscountAmount: Commerce_Price! + itemRelatedDiscountAmount: Commerce_Price! hasAppliedCouponCode: Boolean! getPaymentReference: String! @@ -5844,9 +5844,9 @@ type Commerce_Cart_Delivery { grandTotal: Commerce_Price sumTotalTaxAmount: Commerce_Price subTotalNet: Commerce_Price - sumTotalDiscountAmount: Commerce_Price - sumNonItemRelatedDiscountAmount: Commerce_Price - sumItemRelatedDiscountAmount: Commerce_Price + totalDiscountAmount: Commerce_Price + nonItemRelatedDiscountAmount: Commerce_Price + itemRelatedDiscountAmount: Commerce_Price subTotalGrossWithDiscounts: Commerce_Price subTotalNetWithDiscounts: Commerce_Price! hasItems: Boolean! @@ -9585,14 +9585,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_grandTotal(ctx context.Context, Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.GrandTotal(), nil + return obj.GrandTotal, nil }) if resTmp == nil { @@ -9649,14 +9649,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_subTotalNet(ctx context.Context, Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SubTotalNet(), nil + return obj.SubTotalNet, nil }) if resTmp == nil { @@ -10084,7 +10084,7 @@ func (ec *executionContext) _Commerce_Cart_Cart_getCartTeaser(ctx context.Contex return ec.marshalNCommerce_Cart_Teaser2ᚖflamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋcartᚋdomainᚋcartᚐTeaser(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Cart_sumShippingNet(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Cart_shippingNet(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10095,14 +10095,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumShippingNet(ctx context.Conte Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumShippingNet(), nil + return obj.ShippingNet, nil }) if resTmp == nil { @@ -10116,7 +10116,7 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumShippingNet(ctx context.Conte return ec.marshalNCommerce_Price2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Cart_sumShippingNetWithDiscounts(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Cart_shippingNetWithDiscounts(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10127,14 +10127,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumShippingNetWithDiscounts(ctx Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumShippingNetWithDiscounts(), nil + return obj.ShippingNetWithDiscounts, nil }) if resTmp == nil { @@ -10148,7 +10148,7 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumShippingNetWithDiscounts(ctx return ec.marshalNCommerce_Price2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Cart_sumShippingGross(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Cart_shippingGross(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10159,14 +10159,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumShippingGross(ctx context.Con Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumShippingGross(), nil + return obj.ShippingGross, nil }) if resTmp == nil { @@ -10180,7 +10180,7 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumShippingGross(ctx context.Con return ec.marshalNCommerce_Price2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Cart_sumShippingGrossWithDiscounts(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Cart_shippingGrossWithDiscounts(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10191,14 +10191,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumShippingGrossWithDiscounts(ct Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumShippingGrossWithDiscounts(), nil + return obj.ShippingGrossWithDiscounts, nil }) if resTmp == nil { @@ -10284,14 +10284,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_subTotalGross(ctx context.Contex Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SubTotalGross(), nil + return obj.SubTotalGross, nil }) if resTmp == nil { @@ -10316,14 +10316,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_subTotalGrossWithDiscounts(ctx c Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SubTotalGrossWithDiscounts(), nil + return obj.SubTotalGrossWithDiscounts, nil }) if resTmp == nil { @@ -10348,14 +10348,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_subTotalNetWithDiscounts(ctx con Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SubTotalNetWithDiscounts(), nil + return obj.SubTotalNetWithDiscounts, nil }) if resTmp == nil { @@ -10369,7 +10369,7 @@ func (ec *executionContext) _Commerce_Cart_Cart_subTotalNetWithDiscounts(ctx con return ec.marshalNCommerce_Price2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Cart_sumTotalDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Cart_totalDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10380,14 +10380,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumTotalDiscountAmount(ctx conte Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumTotalDiscountAmount(), nil + return obj.TotalDiscountAmount, nil }) if resTmp == nil { @@ -10401,7 +10401,7 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumTotalDiscountAmount(ctx conte return ec.marshalNCommerce_Price2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Cart_sumNonItemRelatedDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Cart_nonItemRelatedDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10412,14 +10412,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumNonItemRelatedDiscountAmount( Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumNonItemRelatedDiscountAmount(), nil + return obj.NonItemRelatedDiscountAmount, nil }) if resTmp == nil { @@ -10433,7 +10433,7 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumNonItemRelatedDiscountAmount( return ec.marshalNCommerce_Price2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Cart_sumItemRelatedDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Cart_itemRelatedDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Cart) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10444,14 +10444,14 @@ func (ec *executionContext) _Commerce_Cart_Cart_sumItemRelatedDiscountAmount(ctx Object: "Commerce_Cart_Cart", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumItemRelatedDiscountAmount(), nil + return obj.ItemRelatedDiscountAmount, nil }) if resTmp == nil { @@ -11200,14 +11200,14 @@ func (ec *executionContext) _Commerce_Cart_Delivery_subTotalGross(ctx context.Co Object: "Commerce_Cart_Delivery", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SubTotalGross(), nil + return obj.SubTotalGross, nil }) if resTmp == nil { @@ -11229,14 +11229,14 @@ func (ec *executionContext) _Commerce_Cart_Delivery_grandTotal(ctx context.Conte Object: "Commerce_Cart_Delivery", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.GrandTotal(), nil + return obj.GrandTotal, nil }) if resTmp == nil { @@ -11287,14 +11287,14 @@ func (ec *executionContext) _Commerce_Cart_Delivery_subTotalNet(ctx context.Cont Object: "Commerce_Cart_Delivery", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SubTotalNet(), nil + return obj.SubTotalNet, nil }) if resTmp == nil { @@ -11305,7 +11305,7 @@ func (ec *executionContext) _Commerce_Cart_Delivery_subTotalNet(ctx context.Cont return ec.marshalOCommerce_Price2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Delivery_sumTotalDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Delivery) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Delivery_totalDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Delivery) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -11316,14 +11316,14 @@ func (ec *executionContext) _Commerce_Cart_Delivery_sumTotalDiscountAmount(ctx c Object: "Commerce_Cart_Delivery", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumTotalDiscountAmount(), nil + return obj.TotalDiscountAmount, nil }) if resTmp == nil { @@ -11334,7 +11334,7 @@ func (ec *executionContext) _Commerce_Cart_Delivery_sumTotalDiscountAmount(ctx c return ec.marshalOCommerce_Price2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Delivery_sumNonItemRelatedDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Delivery) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Delivery_nonItemRelatedDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Delivery) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -11345,14 +11345,14 @@ func (ec *executionContext) _Commerce_Cart_Delivery_sumNonItemRelatedDiscountAmo Object: "Commerce_Cart_Delivery", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumNonItemRelatedDiscountAmount(), nil + return obj.NonItemRelatedDiscountAmount, nil }) if resTmp == nil { @@ -11363,7 +11363,7 @@ func (ec *executionContext) _Commerce_Cart_Delivery_sumNonItemRelatedDiscountAmo return ec.marshalOCommerce_Price2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Delivery_sumItemRelatedDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Delivery) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Delivery_itemRelatedDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *cart.Delivery) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -11374,14 +11374,14 @@ func (ec *executionContext) _Commerce_Cart_Delivery_sumItemRelatedDiscountAmount Object: "Commerce_Cart_Delivery", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumItemRelatedDiscountAmount(), nil + return obj.ItemRelatedDiscountAmount, nil }) if resTmp == nil { @@ -11403,14 +11403,14 @@ func (ec *executionContext) _Commerce_Cart_Delivery_subTotalGrossWithDiscounts(c Object: "Commerce_Cart_Delivery", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SubTotalGrossWithDiscounts(), nil + return obj.SubTotalGrossWithDiscounts, nil }) if resTmp == nil { @@ -11432,14 +11432,14 @@ func (ec *executionContext) _Commerce_Cart_Delivery_subTotalNetWithDiscounts(ctx Object: "Commerce_Cart_Delivery", Field: field, Args: nil, - IsMethod: true, + IsMethod: false, IsResolver: false, } ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SubTotalNetWithDiscounts(), nil + return obj.SubTotalNetWithDiscounts, nil }) if resTmp == nil { @@ -14121,7 +14121,7 @@ func (ec *executionContext) _Commerce_Cart_Summary_discounts(ctx context.Context return ec.marshalNCommerce_Cart_AppliedDiscounts2ᚖflamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋcartᚋinterfacesᚋgraphqlᚋdtoᚐCartAppliedDiscounts(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Summary_sumAppliedDiscounts(ctx context.Context, field graphql.CollectedField, obj *dto.CartSummary) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Summary_totalDiscountAmount(ctx context.Context, field graphql.CollectedField, obj *dto.CartSummary) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -14139,7 +14139,7 @@ func (ec *executionContext) _Commerce_Cart_Summary_sumAppliedDiscounts(ctx conte ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumAppliedDiscounts(), nil + return obj.TotalDiscountAmount(), nil }) if resTmp == nil { @@ -14150,7 +14150,7 @@ func (ec *executionContext) _Commerce_Cart_Summary_sumAppliedDiscounts(ctx conte return ec.marshalOCommerce_Price2ᚖflamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Summary_sumAppliedGiftCards(ctx context.Context, field graphql.CollectedField, obj *dto.CartSummary) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Summary_totalGiftCardAmount(ctx context.Context, field graphql.CollectedField, obj *dto.CartSummary) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -14168,7 +14168,7 @@ func (ec *executionContext) _Commerce_Cart_Summary_sumAppliedGiftCards(ctx conte ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumAppliedGiftCards(), nil + return obj.TotalGiftCardAmount(), nil }) if resTmp == nil { @@ -14179,7 +14179,7 @@ func (ec *executionContext) _Commerce_Cart_Summary_sumAppliedGiftCards(ctx conte return ec.marshalOCommerce_Price2ᚖflamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋpriceᚋdomainᚐPrice(ctx, field.Selections, res) } -func (ec *executionContext) _Commerce_Cart_Summary_sumGrandTotalWithGiftCards(ctx context.Context, field graphql.CollectedField, obj *dto.CartSummary) (ret graphql.Marshaler) { +func (ec *executionContext) _Commerce_Cart_Summary_grandTotalWithGiftCards(ctx context.Context, field graphql.CollectedField, obj *dto.CartSummary) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -14197,7 +14197,7 @@ func (ec *executionContext) _Commerce_Cart_Summary_sumGrandTotalWithGiftCards(ct ctx = graphql.WithFieldContext(ctx, fc) resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SumGrandTotalWithGiftCards(), nil + return obj.GrandTotalWithGiftCards(), nil }) if resTmp == nil { @@ -27244,23 +27244,23 @@ func (ec *executionContext) _Commerce_Cart_Cart(ctx context.Context, sel ast.Sel if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } - case "sumShippingNet": - out.Values[i] = ec._Commerce_Cart_Cart_sumShippingNet(ctx, field, obj) + case "shippingNet": + out.Values[i] = ec._Commerce_Cart_Cart_shippingNet(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } - case "sumShippingNetWithDiscounts": - out.Values[i] = ec._Commerce_Cart_Cart_sumShippingNetWithDiscounts(ctx, field, obj) + case "shippingNetWithDiscounts": + out.Values[i] = ec._Commerce_Cart_Cart_shippingNetWithDiscounts(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } - case "sumShippingGross": - out.Values[i] = ec._Commerce_Cart_Cart_sumShippingGross(ctx, field, obj) + case "shippingGross": + out.Values[i] = ec._Commerce_Cart_Cart_shippingGross(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } - case "sumShippingGrossWithDiscounts": - out.Values[i] = ec._Commerce_Cart_Cart_sumShippingGrossWithDiscounts(ctx, field, obj) + case "shippingGrossWithDiscounts": + out.Values[i] = ec._Commerce_Cart_Cart_shippingGrossWithDiscounts(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } @@ -27286,18 +27286,18 @@ func (ec *executionContext) _Commerce_Cart_Cart(ctx context.Context, sel ast.Sel if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } - case "sumTotalDiscountAmount": - out.Values[i] = ec._Commerce_Cart_Cart_sumTotalDiscountAmount(ctx, field, obj) + case "totalDiscountAmount": + out.Values[i] = ec._Commerce_Cart_Cart_totalDiscountAmount(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } - case "sumNonItemRelatedDiscountAmount": - out.Values[i] = ec._Commerce_Cart_Cart_sumNonItemRelatedDiscountAmount(ctx, field, obj) + case "nonItemRelatedDiscountAmount": + out.Values[i] = ec._Commerce_Cart_Cart_nonItemRelatedDiscountAmount(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } - case "sumItemRelatedDiscountAmount": - out.Values[i] = ec._Commerce_Cart_Cart_sumItemRelatedDiscountAmount(ctx, field, obj) + case "itemRelatedDiscountAmount": + out.Values[i] = ec._Commerce_Cart_Cart_itemRelatedDiscountAmount(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } @@ -27554,12 +27554,12 @@ func (ec *executionContext) _Commerce_Cart_Delivery(ctx context.Context, sel ast out.Values[i] = ec._Commerce_Cart_Delivery_sumTotalTaxAmount(ctx, field, obj) case "subTotalNet": out.Values[i] = ec._Commerce_Cart_Delivery_subTotalNet(ctx, field, obj) - case "sumTotalDiscountAmount": - out.Values[i] = ec._Commerce_Cart_Delivery_sumTotalDiscountAmount(ctx, field, obj) - case "sumNonItemRelatedDiscountAmount": - out.Values[i] = ec._Commerce_Cart_Delivery_sumNonItemRelatedDiscountAmount(ctx, field, obj) - case "sumItemRelatedDiscountAmount": - out.Values[i] = ec._Commerce_Cart_Delivery_sumItemRelatedDiscountAmount(ctx, field, obj) + case "totalDiscountAmount": + out.Values[i] = ec._Commerce_Cart_Delivery_totalDiscountAmount(ctx, field, obj) + case "nonItemRelatedDiscountAmount": + out.Values[i] = ec._Commerce_Cart_Delivery_nonItemRelatedDiscountAmount(ctx, field, obj) + case "itemRelatedDiscountAmount": + out.Values[i] = ec._Commerce_Cart_Delivery_itemRelatedDiscountAmount(ctx, field, obj) case "subTotalGrossWithDiscounts": out.Values[i] = ec._Commerce_Cart_Delivery_subTotalGrossWithDiscounts(ctx, field, obj) case "subTotalNetWithDiscounts": @@ -28459,12 +28459,12 @@ func (ec *executionContext) _Commerce_Cart_Summary(ctx context.Context, sel ast. if out.Values[i] == graphql.Null { invalids++ } - case "sumAppliedDiscounts": - out.Values[i] = ec._Commerce_Cart_Summary_sumAppliedDiscounts(ctx, field, obj) - case "sumAppliedGiftCards": - out.Values[i] = ec._Commerce_Cart_Summary_sumAppliedGiftCards(ctx, field, obj) - case "sumGrandTotalWithGiftCards": - out.Values[i] = ec._Commerce_Cart_Summary_sumGrandTotalWithGiftCards(ctx, field, obj) + case "totalDiscountAmount": + out.Values[i] = ec._Commerce_Cart_Summary_totalDiscountAmount(ctx, field, obj) + case "totalGiftCardAmount": + out.Values[i] = ec._Commerce_Cart_Summary_totalGiftCardAmount(ctx, field, obj) + case "grandTotalWithGiftCards": + out.Values[i] = ec._Commerce_Cart_Summary_grandTotalWithGiftCards(ctx, field, obj) case "sumTotalDiscountWithGiftCardsAmount": out.Values[i] = ec._Commerce_Cart_Summary_sumTotalDiscountWithGiftCardsAmount(ctx, field, obj) case "hasAppliedDiscounts": diff --git a/test/integrationtest/projecttest/graphql/schema/flamingo.me_flamingo-commerce_v3_cart_interfaces_graphql-Service.graphql b/test/integrationtest/projecttest/graphql/schema/flamingo.me_flamingo-commerce_v3_cart_interfaces_graphql-Service.graphql index c33af899b..a7d9ff85a 100644 --- a/test/integrationtest/projecttest/graphql/schema/flamingo.me_flamingo-commerce_v3_cart_interfaces_graphql-Service.graphql +++ b/test/integrationtest/projecttest/graphql/schema/flamingo.me_flamingo-commerce_v3_cart_interfaces_graphql-Service.graphql @@ -8,9 +8,9 @@ type Commerce_Cart_DecoratedCart { type Commerce_Cart_Summary { discounts: Commerce_Cart_AppliedDiscounts! - sumAppliedDiscounts: Commerce_Price - sumAppliedGiftCards: Commerce_Price - sumGrandTotalWithGiftCards: Commerce_Price + totalDiscountAmount: Commerce_Price + totalGiftCardAmount: Commerce_Price + grandTotalWithGiftCards: Commerce_Price sumTotalDiscountWithGiftCardsAmount: Commerce_Price hasAppliedDiscounts: Boolean! sumTaxes: Commerce_Cart_Taxes @@ -52,10 +52,10 @@ type Commerce_Cart_Cart { getVoucherSavings: Commerce_Price! getCartTeaser: Commerce_Cart_Teaser! - sumShippingNet: Commerce_Price! - sumShippingNetWithDiscounts: Commerce_Price! - sumShippingGross: Commerce_Price! - sumShippingGrossWithDiscounts: Commerce_Price! + shippingNet: Commerce_Price! + shippingNetWithDiscounts: Commerce_Price! + shippingGross: Commerce_Price! + shippingGrossWithDiscounts: Commerce_Price! hasShippingCosts: Boolean! allShippingTitles: [String!] @@ -63,9 +63,9 @@ type Commerce_Cart_Cart { subTotalGross: Commerce_Price! subTotalGrossWithDiscounts: Commerce_Price! subTotalNetWithDiscounts: Commerce_Price! - sumTotalDiscountAmount: Commerce_Price! - sumNonItemRelatedDiscountAmount: Commerce_Price! - sumItemRelatedDiscountAmount: Commerce_Price! + totalDiscountAmount: Commerce_Price! + nonItemRelatedDiscountAmount: Commerce_Price! + itemRelatedDiscountAmount: Commerce_Price! hasAppliedCouponCode: Boolean! getPaymentReference: String! @@ -150,9 +150,9 @@ type Commerce_Cart_Delivery { grandTotal: Commerce_Price sumTotalTaxAmount: Commerce_Price subTotalNet: Commerce_Price - sumTotalDiscountAmount: Commerce_Price - sumNonItemRelatedDiscountAmount: Commerce_Price - sumItemRelatedDiscountAmount: Commerce_Price + totalDiscountAmount: Commerce_Price + nonItemRelatedDiscountAmount: Commerce_Price + itemRelatedDiscountAmount: Commerce_Price subTotalGrossWithDiscounts: Commerce_Price subTotalNetWithDiscounts: Commerce_Price! hasItems: Boolean! diff --git a/test/integrationtest/projecttest/modules/cart/fake_voucher_handler.go b/test/integrationtest/projecttest/modules/cart/fake_voucher_handler.go index 648bcb733..a3e969d29 100644 --- a/test/integrationtest/projecttest/modules/cart/fake_voucher_handler.go +++ b/test/integrationtest/projecttest/modules/cart/fake_voucher_handler.go @@ -6,6 +6,7 @@ import ( domainCart "flamingo.me/flamingo-commerce/v3/cart/domain/cart" "flamingo.me/flamingo-commerce/v3/cart/infrastructure" + "flamingo.me/flamingo-commerce/v3/price/domain" ) type ( @@ -44,6 +45,10 @@ func (f FakeVoucherHandler) ApplyVoucher(ctx context.Context, cart *domainCart.C IsItemRelated: false, SortOrder: 0, }} + cart.Deliveries[delKey].Cartitems[itemKey].RowPriceGrossWithDiscount = domain.NewZero(item.RowPriceGross.Currency()) + cart.Deliveries[delKey].Cartitems[itemKey].RowPriceNetWithDiscount = domain.NewZero(item.RowPriceGross.Currency()) + cart.Deliveries[delKey].Cartitems[itemKey].NonItemRelatedDiscountAmount = item.RowPriceGross.Inverse() + cart.Deliveries[delKey].Cartitems[itemKey].TotalDiscountAmount = item.RowPriceGross.Inverse() } } } diff --git a/test/integrationtest/projecttest/modules/payment/fake_gateway.go b/test/integrationtest/projecttest/modules/payment/fake_gateway.go index 9c7669711..04844a68b 100644 --- a/test/integrationtest/projecttest/modules/payment/fake_gateway.go +++ b/test/integrationtest/projecttest/modules/payment/fake_gateway.go @@ -223,8 +223,8 @@ func (g *FakeGateway) OrderPaymentFromFlow(ctx context.Context, cart *cart.Cart, { TransactionID: correlationID, AdditionalData: nil, - AmountPayed: cart.GrandTotal(), - ValuedAmountPayed: cart.GrandTotal(), + AmountPayed: cart.GrandTotal, + ValuedAmountPayed: cart.GrandTotal, }, }, RawTransactionData: nil, diff --git a/w3cdatalayer/application/factory.go b/w3cdatalayer/application/factory.go index 7703561cd..7645eb467 100644 --- a/w3cdatalayer/application/factory.go +++ b/w3cdatalayer/application/factory.go @@ -215,17 +215,17 @@ func (s Factory) BuildCartData(cart decorator.DecoratedCart) *domain.Cart { cartData := domain.Cart{ CartID: cart.Cart.ID, Price: &domain.CartPrice{ - Currency: cart.Cart.GrandTotal().Currency(), - BasePrice: cart.Cart.SubTotalNet().FloatAmount(), - CartTotal: cart.Cart.GrandTotal().FloatAmount(), - Shipping: cart.Cart.SumShippingNet().FloatAmount(), + Currency: cart.Cart.GrandTotal.Currency(), + BasePrice: cart.Cart.SubTotalNet.FloatAmount(), + CartTotal: cart.Cart.GrandTotal.FloatAmount(), + Shipping: cart.Cart.ShippingNet.FloatAmount(), ShippingMethod: strings.Join(cart.Cart.AllShippingTitles(), "/"), - PriceWithTax: cart.Cart.GrandTotal().FloatAmount(), + PriceWithTax: cart.Cart.GrandTotal.FloatAmount(), }, Attributes: make(map[string]interface{}), } for _, item := range cart.GetAllDecoratedItems() { - itemData := s.buildCartItem(item, cart.Cart.GrandTotal().Currency()) + itemData := s.buildCartItem(item, cart.Cart.GrandTotal.Currency()) cartData.Item = append(cartData.Item, itemData) } return &cartData @@ -246,17 +246,17 @@ func (s Factory) BuildTransactionData(ctx context.Context, cart decorator.Decora transactionData := domain.Transaction{ TransactionID: orderID, Price: &domain.TransactionPrice{ - Currency: cart.Cart.GrandTotal().Currency(), - BasePrice: cart.Cart.GrandTotal().FloatAmount(), - TransactionTotal: cart.Cart.GrandTotal().FloatAmount(), - Shipping: cart.Cart.SumShippingNet().FloatAmount(), + Currency: cart.Cart.GrandTotal.Currency(), + BasePrice: cart.Cart.GrandTotal.FloatAmount(), + TransactionTotal: cart.Cart.GrandTotal.FloatAmount(), + Shipping: cart.Cart.ShippingNet.FloatAmount(), ShippingMethod: strings.Join(cart.Cart.AllShippingTitles(), "/"), }, Profile: profile, Attributes: make(map[string]interface{}), } for _, item := range decoratedItems { - itemData := s.buildCartItem(item, cart.Cart.GrandTotal().Currency()) + itemData := s.buildCartItem(item, cart.Cart.GrandTotal.Currency()) transactionData.Item = append(transactionData.Item, itemData) } return &transactionData